diff --git a/changes.md b/changes.md
index 54c5c0cc15..e873316df9 100644
--- a/changes.md
+++ b/changes.md
@@ -2,11 +2,15 @@ Changes log
===========
- 2.6 Release Candidate 1 (??-03-2025)
+ - Enhancements
+ - Added MultiPartRepresentation to Jetty extension to support generation and parsing.
+ - Added support for the "charset" parameter in HTTP BASIC challenges. Reported by Marc Lafon.
+ - Added MediaType constructors to help with cloning and customization needs.
- Misc
- - Upgrade the thymeleaf library to 3.1.3.RELEASE.
- - Upgraded the Slf4j library to 5.12.0.
- - Upgraded the GWT libraries to version 2.12.2.
- - Upgraded the Jetty library to version 2.0.17.
+ - Upgraded Thymeleaf library to 3.1.3.RELEASE.
+ - Upgraded Slf4j library to 5.12.0.
+ - Upgraded GWT libraries to version 2.12.2.
+ - Upgraded Jetty library to version 2.0.17.
- 2.6 Milestone 2 (02-03-2025)
- Enhancements
diff --git a/org.restlet.java/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java b/org.restlet.java/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java
index 5ea7961cd6..4c74e7e91f 100644
--- a/org.restlet.java/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java
+++ b/org.restlet.java/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java
@@ -9,7 +9,6 @@
package org.restlet.ext.crypto;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.restlet.*;
import org.restlet.data.CookieSetting;
diff --git a/org.restlet.java/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java b/org.restlet.java/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java
index c1aa98fb76..013cc4ed5d 100644
--- a/org.restlet.java/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java
+++ b/org.restlet.java/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java
@@ -18,7 +18,6 @@
import java.io.FileWriter;
import java.nio.file.Files;
import java.util.Map;
-import java.util.TreeMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/org.restlet.java/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java b/org.restlet.java/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java
index aea896a2a8..1ec6a46c68 100644
--- a/org.restlet.java/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java
@@ -19,7 +19,6 @@
import java.util.Date;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/MultiPartRepresentation.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/MultiPartRepresentation.java
new file mode 100644
index 0000000000..0a4e1e138a
--- /dev/null
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/MultiPartRepresentation.java
@@ -0,0 +1,333 @@
+/**
+ * Copyright 2005-2024 Qlik
+ *
+ * The contents of this 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.
+ */
+
+package org.restlet.ext.jetty;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+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;
+import org.eclipse.jetty.http.MultiPartConfig;
+import org.eclipse.jetty.http.MultiPartFormData;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.io.content.InputStreamContentSource;
+import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.Promise;
+import org.restlet.data.MediaType;
+import org.restlet.representation.InputRepresentation;
+import org.restlet.representation.Representation;
+
+/**
+ * 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.
+ * @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,
+ 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.
+ */
+ public static String getBoundary(MediaType mediaType) {
+ final String result;
+
+ if (mediaType != null) {
+ result = mediaType.getParameters().getFirstValue("boundary");
+ } else {
+ result = null;
+ }
+
+ return result;
+ }
+
+ /**
+ * Sets a boundary to an existing media type. If the original mediatype
+ * 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.
+ * @return The updated media type.
+ */
+ 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);
+ } else {
+ result = new MediaType(mediaType, "boundary", boundary);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * 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}.
+ *
+ * @param parts The source parts to use when generating the representation.
+ */
+ public MultiPartRepresentation(List parts) {
+ this(MultiPart.generateBoundary(null, 24), 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}.
+ *
+ * @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.
+ */
+ 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}.
+ *
+ * @param parts The source parts to use when generating the representation.
+ */
+ public MultiPartRepresentation(Part... parts) {
+ this(Arrays.asList(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.
+ * @throws IOException
+ */
+ 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.
+ * @throws IOException
+ */
+ 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.
+ * @param multiPartEntity The multipart entity to parse.
+ * @param config The multipart configuration.
+ * @throws IOException
+ */
+ 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");
+
+ if (this.boundary != null) {
+ if (multiPartEntity != null) {
+ 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,
+ new Promise.Invocable<>() {
+ @Override
+ public void failed(Throwable failure) {
+ throw new IllegalStateException(
+ "Unable to parse the multipart form data representation",
+ failure);
+ }
+
+ @Override
+ public InvocationType getInvocationType() {
+ return InvocationType.BLOCKING;
+ }
+
+ @Override
+ public void succeeded(
+ MultiPartFormData.Parts parts) {
+ // Store the resulting parts
+ MultiPartRepresentation.this.parts = new ArrayList<>();
+ parts.iterator().forEachRemaining(
+ part -> MultiPartRepresentation.this.parts
+ .add(part));
+ }
+ });
+ } else {
+ throw new IllegalArgumentException(
+ "The multipart entity can't be null");
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "The content type must have a \"boundary\" parameter");
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "The content type must be \"multipart/form-data\" with a \"boundary\" parameter");
+ }
+ }
+
+ /**
+ * 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.
+ */
+ 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}.
+ *
+ * @param parts The source parts to use when generating the representation.
+ */
+ public MultiPartRepresentation(String boundary, Part... parts) {
+ this(boundary, Arrays.asList(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.
+ */
+ public String getBoundary() {
+ return boundary;
+ }
+
+ /**
+ * Returns the wrapped multipart form data either parsed or to be generated.
+ *
+ * @return The wrapped multipart form data either parsed or to be generated.
+ */
+ public List getParts() {
+ return parts;
+ }
+
+ /**
+ * 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
+ public InputStream getStream() throws IOException {
+ if (getBoundary() == null) {
+ throw new IllegalArgumentException("The boundary can't be null");
+ }
+
+ MultiPartFormData.ContentSource content = new MultiPartFormData.ContentSource(
+ getBoundary());
+
+ for (Part part : this.parts) {
+ content.addPart(part);
+ }
+
+ content.close();
+ setStream(null);
+ return Content.Source.asInputStream(content);
+ }
+
+ /**
+ * 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));
+ } else {
+ setMediaType(setBoundary(getMediaType(), boundary));
+ }
+ }
+
+}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/MultiPartFormTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/MultiPartFormTestCase.java
deleted file mode 100644
index f2f12ccba0..0000000000
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/MultiPartFormTestCase.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/**
- * Copyright 2005-2024 Qlik
- *
- * The contents of this 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.
- */
-
-package org.restlet.ext.jetty;
-
-import org.junit.jupiter.api.Test;
-
-/**
- * Test case for the {@link FormDataSet} class in multipart mode.
- *
- * @author Jerome Louvel
- */
-public class MultiPartFormTestCase {
-
- /*
- TODO restore test about support of multi part representations.
-
-
- @Test
- public void testWrite() throws IOException {
-
- // considered as a simple field entry
- Representation textFile = new EmptyRepresentation();
- textFile.setMediaType(MediaType.TEXT_PLAIN);
-
- // considered as a file
- Representation textFile2 = new StringRepresentation("test",
- MediaType.TEXT_PLAIN);
- textFile2.setDisposition(new Disposition());
- textFile2.getDisposition().setFilename("test.txt");
-
- // considered as a file
- Representation file = new EmptyRepresentation();
- file.setMediaType(MediaType.APPLICATION_OCTET_STREAM);
-
- String boundary = "-----------------------------1294919323195";
- String boundaryBis = "--" + boundary;
- String expected;
-
- FormDataSet form = new FormDataSet(boundary);
- form.getEntries().add(new FormData("number", "5555555555"));
- form.getEntries().add(new FormData("clip", "rickroll"));
- form.getEntries().add(new FormData("upload_file", file));
- form.getEntries().add(new FormData("upload_textfile", textFile));
- form.getEntries().add(new FormData("upload_textfile2", textFile2));
- form.getEntries().add(new FormData("tos", "agree"));
-
- expected = boundaryBis
- + "\r\n"
- + "Content-Disposition: form-data; name=\"number\"\r\n"
- + "\r\n"
- + "5555555555\r\n"
- + boundaryBis
- + "\r\n"
- + "Content-Disposition: form-data; name=\"clip\"\r\n"
- + "\r\n"
- + "rickroll\r\n"
- + boundaryBis
- + "\r\n"
- + "Content-Disposition: form-data; name=\"upload_file\"; filename=\"\"\r\n"
- + "Content-Type: application/octet-stream\r\n"
- + "\r\n"
- + "\r\n"
- + boundaryBis
- + "\r\n"
- + "Content-Disposition: form-data; name=\"upload_textfile\"\r\n"
- + "\r\n"
- + "\r\n"
- + boundaryBis
- + "\r\n"
- + "Content-Disposition: form-data; name=\"upload_textfile2\"; filename=\"test.txt\"\r\n"
- + "Content-Type: text/plain; charset=UTF-8\r\n" + "\r\n"
- + "test" + "\r\n" + boundaryBis + "\r\n"
- + "Content-Disposition: form-data; name=\"tos\"\r\n" + "\r\n"
- + "agree\r\n" + boundaryBis + "--\r\n";
- assertEquals(expected, form.getText());
- }
- */
-
- /**
- * Tests the multipart content-type.
- */
- @Test
- public void testContentType() {
- /*
-TODO restore test of Form class
- FormDataSet form = null;
-
- form = new FormDataSet();
- form.setMultipart(true);
- assertTrue(form.getMediaType().equals(MediaType.MULTIPART_FORM_DATA,
- true));
-
- form = new FormDataSet("test");
- assertTrue(form.isMultipart());
- assertTrue(form.getMediaType().equals(MediaType.MULTIPART_FORM_DATA,
- true));
- assertEquals(
- form.getMediaType().getParameters().getFirstValue("boundary"),
- "test");
- form = new FormDataSet();
-
- form.setMultipartBoundary("test2");
- assertTrue(form.isMultipart());
- assertTrue(form.getMediaType().equals(MediaType.MULTIPART_FORM_DATA,
- true));
- assertEquals(
- form.getMediaType().getParameters().getFirstValue("boundary"),
- "test2");
-
- */
- }
-}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/MultiPartRepresentationTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/MultiPartRepresentationTestCase.java
new file mode 100644
index 0000000000..70a6760edd
--- /dev/null
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/MultiPartRepresentationTestCase.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright 2005-2024 Qlik
+ *
+ * The contents of this 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.
+ */
+
+package org.restlet.ext.jetty;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+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;
+import org.eclipse.jetty.http.MultiPart.Part;
+import org.junit.jupiter.api.Test;
+import org.restlet.data.MediaType;
+import org.restlet.representation.StringRepresentation;
+
+/**
+ * Test case for the {@link MultiPartRepresentation} class in multipart mode.
+ *
+ * @author Jerome Louvel
+ */
+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);
+
+ MultiPart.ContentSourcePart contentSourcePart = new MultiPart.ContentSourcePart(
+ "field", null, HttpFields.EMPTY,
+ new StringRequestContent("foo"));
+
+ final String boundary = "-----------------------------1294919323195";
+
+ MultiPartRepresentation rep = new MultiPartRepresentation(
+ contentSourcePart, filePart);
+ rep.setBoundary(boundary);
+
+ final String expected = """
+ --%s\r
+ Content-Disposition: form-data; name="field"\r
+ \r
+ foo\r
+ --%s\r
+ Content-Disposition: form-data; name="icon"; filename="text.txt"\r
+ \r
+ this is the content of the file\r
+ --%s--\r
+ """
+ .replace("%s", boundary);
+ assertEquals(expected, rep.getText());
+ }
+
+ /**
+ * 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);
+ rep.setBoundary("myActualBoundary");
+ assertEquals("multipart/form-data; boundary=myActualBoundary",
+ rep.getMediaType().toString());
+ }
+
+ @Test
+ public void testParseIntoParts() throws IOException {
+ final String boundary = "-----------------------------1294919323195";
+ final String multipartEntityContent = """
+ --%s\r
+ Content-Disposition: form-data; name="field"\r
+ \r
+ foo\r
+ --%s\r
+ Content-Disposition: form-data; name="icon"; filename="text.txt"\r
+ \r
+ this is the content of the file\r
+ --%s--\r
+ """
+ .replace("%s", boundary);
+
+ 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);
+
+ Part part1 = rep.getParts().get(0);
+ assertEquals("field", part1.getName());
+ assertNull(part1.getFileName());
+ assertEquals(3, part1.getLength());
+ assertEquals("foo", part1.getContentAsString(null));
+
+ Part part2 = rep.getParts().get(1);
+ assertEquals("icon", part2.getName());
+ assertEquals("text.txt", part2.getFileName());
+ assertEquals(31, part2.getLength());
+ assertEquals("this is the content of the file",
+ part2.getContentAsString(null));
+
+ }
+
+}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java
index 0ee1b8d7a7..43d56eab66 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java
@@ -16,7 +16,6 @@
import org.restlet.representation.Variant;
import org.restlet.resource.ServerResource;
import org.restlet.routing.Router;
-import org.restlet.util.Series;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/org.restlet.java/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java b/org.restlet.java/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java
index b16db8c473..e8566469a4 100644
--- a/org.restlet.java/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java
+++ b/org.restlet.java/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java
@@ -10,7 +10,6 @@
package org.restlet.ext.spring;
import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.restlet.Request;
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/MediaType.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/MediaType.java
index ed6117c703..0e2126df3a 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/MediaType.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/MediaType.java
@@ -30,854 +30,1011 @@
*/
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 = "()<>@,;:/[]?=\\\"";
+ /**
+ * 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)}.
- * Keep the underscore for the ordering.
- */
- private static volatile Map _types = null;
+ /**
+ * 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 ALL = register("*/*", "All media");
- public static final MediaType APPLICATION_ALL = register("application/*", "All application documents");
+ 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_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_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_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_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_ATOMPUB_SERVICE = register(
+ "application/atomsvc+xml", "Atom service document");
- public static final MediaType APPLICATION_CAB = register("application/vnd.ms-cab-compressed",
- "Microsoft Cabinet archive");
+ public static final MediaType APPLICATION_CAB = register(
+ "application/vnd.ms-cab-compressed", "Microsoft Cabinet archive");
- public static final MediaType APPLICATION_COMPRESS = register("application/x-compress", "Compressed file");
+ public static final MediaType APPLICATION_COMPRESS = register(
+ "application/x-compress", "Compressed file");
- public static final MediaType APPLICATION_ECORE = register("application/x-ecore+xmi+xml", "EMOF ECore metamodel");
+ public static final MediaType APPLICATION_ECORE = register(
+ "application/x-ecore+xmi+xml", "EMOF ECore metamodel");
- public static final MediaType APPLICATION_EXCEL = register("application/vnd.ms-excel", "Microsoft Excel document");
+ public static final MediaType APPLICATION_EXCEL = register(
+ "application/vnd.ms-excel", "Microsoft Excel document");
- public static final MediaType APPLICATION_FLASH = register("application/x-shockwave-flash",
- "Shockwave Flash object");
+ public static final MediaType APPLICATION_FLASH = register(
+ "application/x-shockwave-flash", "Shockwave Flash object");
- public static final MediaType APPLICATION_GNU_TAR = register("application/x-gtar", "GNU Tar archive");
+ public static final MediaType APPLICATION_GNU_TAR = register(
+ "application/x-gtar", "GNU Tar archive");
- public static final MediaType APPLICATION_GNU_ZIP = register("application/x-gzip", "GNU Zip archive");
+ public static final MediaType APPLICATION_GNU_ZIP = register(
+ "application/x-gzip", "GNU Zip archive");
- public static final MediaType APPLICATION_HTTP_COOKIES = register("application/x-http-cookies", "HTTP cookies");
+ public static final MediaType APPLICATION_HTTP_COOKIES = register(
+ "application/x-http-cookies", "HTTP cookies");
- public static final MediaType APPLICATION_JAVA = register("application/java", "Java class");
+ public static final MediaType APPLICATION_JAVA = register(
+ "application/java", "Java class");
- public static final MediaType APPLICATION_JAVA_ARCHIVE = register("application/java-archive", "Java archive");
+ public static final MediaType APPLICATION_JAVA_ARCHIVE = register(
+ "application/java-archive", "Java archive");
- public static final MediaType APPLICATION_JAVA_OBJECT = register("application/x-java-serialized-object",
- "Java serialized object");
+ public static final MediaType APPLICATION_JAVA_OBJECT = register(
+ "application/x-java-serialized-object", "Java serialized object");
- 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_JAVA_OBJECT_GWT = register(
+ "text/x-gwt-rpc", "Java serialized object (using GWT-RPC encoder)");
- 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_JAVA_OBJECT_XML = register(
+ "text/x-gwt-rpc+xml",
+ "Java serialized object (using JavaBeans XML encoder)");
- public static final MediaType APPLICATION_JAVASCRIPT = register("application/x-javascript", "Javascript document");
+ public static final MediaType APPLICATION_JAVASCRIPT = register(
+ "application/x-javascript", "Javascript document");
- public static final MediaType APPLICATION_JNLP = register("application/x-java-jnlp-file", "JNLP");
+ public static final MediaType APPLICATION_JNLP = register(
+ "application/x-java-jnlp-file", "JNLP");
- public static final MediaType APPLICATION_JSON = register("application/json",
- "JavaScript Object Notation document");
+ public static final MediaType APPLICATION_JSON = register(
+ "application/json", "JavaScript Object Notation document");
- public static final MediaType APPLICATION_JSON_ACTIVITY = register("application/activity+json",
- "Activity Streams JSON document");
+ public static final MediaType APPLICATION_JSON_ACTIVITY = register(
+ "application/activity+json", "Activity Streams JSON document");
- public static final MediaType APPLICATION_JSON_PATCH = register("application/json-patch", "JSON patch document");
+ public static final MediaType APPLICATION_JSON_PATCH = register(
+ "application/json-patch", "JSON patch document");
- public static final MediaType APPLICATION_JSON_SMILE = register("application/x-json-smile",
- "JavaScript Object Notation smile document");
+ public static final MediaType APPLICATION_JSON_SMILE = register(
+ "application/x-json-smile",
+ "JavaScript Object Notation smile document");
- public static final MediaType APPLICATION_KML = register("application/vnd.google-earth.kml+xml",
- "Google Earth/Maps KML document");
+ public static final MediaType APPLICATION_KML = register(
+ "application/vnd.google-earth.kml+xml",
+ "Google Earth/Maps KML document");
- public static final MediaType APPLICATION_KMZ = register("application/vnd.google-earth.kmz",
- "Google Earth/Maps KMZ document");
+ public static final MediaType APPLICATION_KMZ = register(
+ "application/vnd.google-earth.kmz",
+ "Google Earth/Maps KMZ document");
- public static final MediaType APPLICATION_LATEX = register("application/x-latex", "LaTeX");
+ public static final MediaType APPLICATION_LATEX = register(
+ "application/x-latex", "LaTeX");
- public static final MediaType APPLICATION_MAC_BINHEX40 = register("application/mac-binhex40", "Mac binhex40");
+ public static final MediaType APPLICATION_MAC_BINHEX40 = register(
+ "application/mac-binhex40", "Mac binhex40");
- public static final MediaType APPLICATION_MATHML = register("application/mathml+xml", "MathML XML document");
+ public static final MediaType APPLICATION_MATHML = register(
+ "application/mathml+xml", "MathML XML document");
- public static final MediaType APPLICATION_MSML = register("application/msml+xml", "Media Server Markup Language");
+ public static final MediaType APPLICATION_MSML = register(
+ "application/msml+xml", "Media Server Markup Language");
- 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_DOCM = register(
+ "application/vnd.ms-word.document.macroEnabled.12",
+ "Office Word 2007 macro-enabled 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_MSOFFICE_DOCX = register(
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "Microsoft Office Word 2007 document");
- 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_DOTM = register(
+ "application/vnd.ms-word.template.macroEnabled.12",
+ "Office Word 2007 macro-enabled document template");
- public static final MediaType APPLICATION_MSOFFICE_DOTX = register(
- "application/vnd.openxmlformats-officedocument.wordprocessingml.template", "Office Word 2007 template");
+ public static final MediaType APPLICATION_MSOFFICE_DOTX = register(
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+ "Office Word 2007 template");
- public static final MediaType APPLICATION_MSOFFICE_ONETOC = register("application/onenote",
- "Microsoft Office OneNote 2007 TOC");
+ public static final MediaType APPLICATION_MSOFFICE_ONETOC = register(
+ "application/onenote", "Microsoft Office OneNote 2007 TOC");
- public static final MediaType APPLICATION_MSOFFICE_ONETOC2 = register("application/onenote",
- "Office OneNote 2007 TOC");
+ public static final MediaType APPLICATION_MSOFFICE_ONETOC2 = register(
+ "application/onenote", "Office OneNote 2007 TOC");
- 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_POTM = register(
+ "application/vnd.ms-powerpoint.template.macroEnabled.12",
+ "Office PowerPoint 2007 macro-enabled presentation template");
- public static final MediaType APPLICATION_MSOFFICE_POTX = register(
- "application/vnd.openxmlformats-officedocument.presentationml.template", "Office PowerPoint 2007 template");
+ public static final MediaType APPLICATION_MSOFFICE_POTX = register(
+ "application/vnd.openxmlformats-officedocument.presentationml.template",
+ "Office PowerPoint 2007 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_PPAM = register(
+ "application/vnd.ms-powerpoint.addin.macroEnabled.12",
+ "Office PowerPoint 2007 add-in");
- 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_PPSM = register(
+ "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
+ "Office PowerPoint 2007 macro-enabled slide show");
- 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_PPSX = register(
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+ "Office PowerPoint 2007 slide show");
- 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_PPTM = register(
+ "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
+ "Office PowerPoint 2007 macro-enabled presentation");
- 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_PPTX = register(
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ "Microsoft Office PowerPoint 2007 presentation");
- 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_SLDM = register(
+ "application/vnd.ms-powerpoint.slide.macroEnabled.12",
+ "Office PowerPoint 2007 macro-enabled slide");
- public static final MediaType APPLICATION_MSOFFICE_SLDX = register(
- "application/vnd.openxmlformats-officedocument.presentationml.slide", "Office PowerPoint 2007 slide");
+ public static final MediaType APPLICATION_MSOFFICE_SLDX = register(
+ "application/vnd.openxmlformats-officedocument.presentationml.slide",
+ "Office PowerPoint 2007 slide");
- 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_XLAM = register(
+ "application/vnd.ms-excel.addin.macroEnabled.12",
+ "Office Excel 2007 add-in");
- 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_XLSB = register(
+ "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
+ "Office Excel 2007 binary workbook");
- 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_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_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_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_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_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_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_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_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_ODG = register(
+ "application/vnd.oasis.opendocument.graphics",
+ "OpenDocument Drawing");
- public static final MediaType APPLICATION_OPENOFFICE_ODI = register("application/vnd.oasis.opendocument.image",
- "OpenDocument Image ");
+ public static final MediaType APPLICATION_OPENOFFICE_ODI = register(
+ "application/vnd.oasis.opendocument.image", "OpenDocument Image ");
- public static final MediaType APPLICATION_OPENOFFICE_ODM = register(
- "application/vnd.oasis.opendocument.text-master", "OpenDocument Master Document");
+ public static final MediaType APPLICATION_OPENOFFICE_ODM = register(
+ "application/vnd.oasis.opendocument.text-master",
+ "OpenDocument Master Document");
- public static final MediaType APPLICATION_OPENOFFICE_ODP = register(
- "application/vnd.oasis.opendocument.presentation", "OpenDocument Presentation ");
+ public static final MediaType APPLICATION_OPENOFFICE_ODP = register(
+ "application/vnd.oasis.opendocument.presentation",
+ "OpenDocument Presentation ");
- public static final MediaType APPLICATION_OPENOFFICE_ODS = register(
- "application/vnd.oasis.opendocument.spreadsheet", "OpenDocument Spreadsheet");
+ public static final MediaType APPLICATION_OPENOFFICE_ODS = register(
+ "application/vnd.oasis.opendocument.spreadsheet",
+ "OpenDocument Spreadsheet");
- public static final MediaType APPLICATION_OPENOFFICE_ODT = register("application/vnd.oasis.opendocument.text ",
- "OpenDocument Text");
+ public static final MediaType APPLICATION_OPENOFFICE_ODT = register(
+ "application/vnd.oasis.opendocument.text ", "OpenDocument Text");
- public static final MediaType APPLICATION_OPENOFFICE_OTG = register(
- "application/vnd.oasis.opendocument.graphics-template", "OpenDocument Drawing Template");
+ public static final MediaType APPLICATION_OPENOFFICE_OTG = register(
+ "application/vnd.oasis.opendocument.graphics-template",
+ "OpenDocument Drawing Template");
- public static final MediaType APPLICATION_OPENOFFICE_OTH = register("application/vnd.oasis.opendocument.text-web",
- "HTML Document Template");
+ public static final MediaType APPLICATION_OPENOFFICE_OTH = register(
+ "application/vnd.oasis.opendocument.text-web",
+ "HTML Document Template");
- public static final MediaType APPLICATION_OPENOFFICE_OTP = register(
- "application/vnd.oasis.opendocument.presentation-template", "OpenDocument Presentation Template");
+ public static final MediaType APPLICATION_OPENOFFICE_OTP = register(
+ "application/vnd.oasis.opendocument.presentation-template",
+ "OpenDocument Presentation Template");
- public static final MediaType APPLICATION_OPENOFFICE_OTS = register(
- "application/vnd.oasis.opendocument.spreadsheet-template", "OpenDocument Spreadsheet Template");
+ public static final MediaType APPLICATION_OPENOFFICE_OTS = register(
+ "application/vnd.oasis.opendocument.spreadsheet-template",
+ "OpenDocument Spreadsheet Template");
- public static final MediaType APPLICATION_OPENOFFICE_OTT = register(
- "application/vnd.oasis.opendocument.text-template", "OpenDocument Text Template");
+ public static final MediaType APPLICATION_OPENOFFICE_OTT = register(
+ "application/vnd.oasis.opendocument.text-template",
+ "OpenDocument Text Template");
- public static final MediaType APPLICATION_OPENOFFICE_OXT = register("application/vnd.openofficeorg.extension",
- "OpenOffice.org extension");
+ public static final MediaType APPLICATION_OPENOFFICE_OXT = register(
+ "application/vnd.openofficeorg.extension",
+ "OpenOffice.org extension");
- public static final MediaType APPLICATION_PDF = register("application/pdf", "Adobe PDF document");
+ public static final MediaType APPLICATION_PDF = register("application/pdf",
+ "Adobe PDF document");
- public static final MediaType APPLICATION_POSTSCRIPT = register("application/postscript", "Postscript document");
+ public static final MediaType APPLICATION_POSTSCRIPT = register(
+ "application/postscript", "Postscript document");
- public static final MediaType APPLICATION_POWERPOINT = register("application/vnd.ms-powerpoint",
- "Microsoft Powerpoint document");
+ public static final MediaType APPLICATION_POWERPOINT = register(
+ "application/vnd.ms-powerpoint", "Microsoft Powerpoint document");
- public static final MediaType APPLICATION_PROJECT = register("application/vnd.ms-project",
- "Microsoft Project document");
+ public static final MediaType APPLICATION_PROJECT = register(
+ "application/vnd.ms-project", "Microsoft Project document");
- public static final MediaType APPLICATION_RDF_TRIG = register("application/x-trig",
- "Plain text 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_RDF_TRIX = register("application/trix",
- "Simple XML serialized Resource Description Framework document");
+ public static final MediaType APPLICATION_RDF_TRIX = register(
+ "application/trix",
+ "Simple XML serialized Resource Description Framework 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_XML = register(
+ "application/rdf+xml",
+ "Normalized XML 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_RELAXNG_COMPACT = register(
+ "application/relax-ng-compact-syntax",
+ "Relax NG Schema document, Compact syntax");
- public static final MediaType APPLICATION_RELAXNG_XML = register("application/x-relax-ng+xml",
- "Relax NG Schema document, XML syntax");
+ public static final MediaType APPLICATION_RELAXNG_XML = register(
+ "application/x-relax-ng+xml",
+ "Relax NG Schema document, XML syntax");
- public static final MediaType APPLICATION_RSS = register("application/rss+xml",
- "Really Simple Syndication document");
+ public static final MediaType APPLICATION_RSS = register(
+ "application/rss+xml", "Really Simple Syndication document");
- public static final MediaType APPLICATION_RTF = register("application/rtf", "Rich Text Format document");
+ public static final MediaType APPLICATION_RTF = register("application/rtf",
+ "Rich Text Format document");
- public static final MediaType APPLICATION_SDP = register("application/sdp", "Session Description Protocol");
+ public static final MediaType APPLICATION_SDP = register("application/sdp",
+ "Session Description Protocol");
- public static final MediaType APPLICATION_SPARQL_RESULTS_JSON = register("application/sparql-results+json",
- "SPARQL Query Results JSON document");
+ public static final MediaType APPLICATION_SPARQL_RESULTS_JSON = register(
+ "application/sparql-results+json",
+ "SPARQL Query Results JSON document");
- public static final MediaType APPLICATION_SPARQL_RESULTS_XML = register("application/sparql-results+xml",
- "SPARQL Query Results XML document");
+ public static final MediaType APPLICATION_SPARQL_RESULTS_XML = register(
+ "application/sparql-results+xml",
+ "SPARQL Query Results XML document");
- public static final MediaType APPLICATION_SPSS_SAV = register("application/x-spss-sav", "SPSS Data");
+ public static final MediaType APPLICATION_SPSS_SAV = register(
+ "application/x-spss-sav", "SPSS Data");
- public static final MediaType APPLICATION_SPSS_SPS = register("application/x-spss-sps", "SPSS Script Syntax");
+ public static final MediaType APPLICATION_SPSS_SPS = register(
+ "application/x-spss-sps", "SPSS Script Syntax");
- public static final MediaType APPLICATION_STATA_STA = register("application/x-stata", "Stata data file");
+ public static final MediaType APPLICATION_STATA_STA = register(
+ "application/x-stata", "Stata data file");
- public static final MediaType APPLICATION_STUFFIT = register("application/x-stuffit", "Stuffit archive");
+ public static final MediaType APPLICATION_STUFFIT = register(
+ "application/x-stuffit", "Stuffit archive");
- public static final MediaType APPLICATION_TAR = register("application/x-tar", "Tar archive");
+ public static final MediaType APPLICATION_TAR = register(
+ "application/x-tar", "Tar archive");
- public static final MediaType APPLICATION_TEX = register("application/x-tex", "Tex file");
+ public static final MediaType APPLICATION_TEX = register(
+ "application/x-tex", "Tex file");
- public static final MediaType APPLICATION_TROFF_MAN = register("application/x-troff-man", "LaTeX");
+ public static final MediaType APPLICATION_TROFF_MAN = register(
+ "application/x-troff-man", "LaTeX");
- public static final MediaType APPLICATION_VOICEXML = register("application/voicexml+xml", "VoiceXML");
+ public static final MediaType APPLICATION_VOICEXML = register(
+ "application/voicexml+xml", "VoiceXML");
- public static final MediaType APPLICATION_W3C_SCHEMA = register("application/x-xsd+xml", "W3C XML Schema document");
+ public static final MediaType APPLICATION_W3C_SCHEMA = register(
+ "application/x-xsd+xml", "W3C XML Schema document");
- public static final MediaType APPLICATION_W3C_XSLT = register("application/xslt+xml", "W3C XSLT Stylesheet");
+ public static final MediaType APPLICATION_W3C_XSLT = register(
+ "application/xslt+xml", "W3C XSLT Stylesheet");
- public static final MediaType APPLICATION_WADL = register("application/vnd.sun.wadl+xml",
- "Web Application Description Language document");
+ public static final MediaType APPLICATION_WADL = register(
+ "application/vnd.sun.wadl+xml",
+ "Web Application Description Language document");
- public static final MediaType APPLICATION_WORD = register("application/msword", "Microsoft Word document");
+ public static final MediaType APPLICATION_WORD = register(
+ "application/msword", "Microsoft Word document");
- public static final MediaType APPLICATION_WWW_FORM = register("application/x-www-form-urlencoded",
- "Web form (URL encoded)");
+ public static final MediaType APPLICATION_WWW_FORM = register(
+ "application/x-www-form-urlencoded", "Web form (URL encoded)");
- public static final MediaType APPLICATION_XHTML = register("application/xhtml+xml", "XHTML document");
+ public static final MediaType APPLICATION_XHTML = register(
+ "application/xhtml+xml", "XHTML document");
- public static final MediaType APPLICATION_XMI = register("application/xmi+xml", "XMI document");
+ public static final MediaType APPLICATION_XMI = register(
+ "application/xmi+xml", "XMI document");
- public static final MediaType APPLICATION_XML = register("application/xml", "XML document");
+ public static final MediaType APPLICATION_XML = register("application/xml",
+ "XML document");
- public static final MediaType APPLICATION_XML_DTD = register("application/xml-dtd", "XML DTD");
+ public static final MediaType APPLICATION_XML_DTD = register(
+ "application/xml-dtd", "XML DTD");
- public static final MediaType APPLICATION_XQUERY = register("application/xquery", "XQuery 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_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_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_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_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_ALL = register("model/*", "All models");
- public static final MediaType MODEL_VRML = register("model/vrml", "VRML");
+ 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_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_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_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");
-
- /**
- * 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
- *
- *
- * @param mediaTypes An array of media types.
- * @return The most concrete MediaType.
- * @throws IllegalArgumentException If the array is null or empty.
- */
- public static MediaType getMostSpecific(MediaType... mediaTypes) throws IllegalArgumentException {
- if ((mediaTypes == null) || (mediaTypes.length == 0)) {
- throw new IllegalArgumentException("You must give at least one MediaType");
- }
-
- if (mediaTypes.length == 1) {
- return mediaTypes[0];
- }
-
- MediaType mostSpecific = mediaTypes[0];
-
- for (int i = 1; i < mediaTypes.length; i++) {
- MediaType mediaType = mediaTypes[i];
-
- if (mediaType != null) {
- if (mediaType.getMainType().equals("*")) {
- continue;
- }
-
- if (mostSpecific.getMainType().equals("*")) {
- mostSpecific = mediaType;
- continue;
- }
-
- if (mostSpecific.getSubType().contains("*")) {
- mostSpecific = mediaType;
- continue;
- }
- }
- }
-
- 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.
- *
- * @param token Token to normalize.
- * @return The normalized token.
- * @throws IllegalArgumentException if token is not legal.
- */
- private static String normalizeToken(String token) {
- int length;
- char c;
-
- // Makes sure we're not dealing with a "*" token.
- token = token.trim();
- if (token.isEmpty() || "*".equals(token))
- return "*";
-
- // Makes sure the token is RFC compliant.
- length = token.length();
- for (int i = 0; i < length; i++) {
- c = token.charAt(i);
- if (c <= 32 || c >= 127 || _TSPECIALS.indexOf(c) != -1)
- throw new IllegalArgumentException("Illegal token: " + token);
- }
-
- return token;
- }
-
- /**
- * Normalizes the specified media type.
- *
- * @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) {
- int slashIndex;
- int colonIndex;
- String mainType;
- String subType;
- StringBuilder params = null;
-
- // Ignore null names (backward compatibility).
- if (name == null)
- return null;
-
- // Check presence of parameters
- if ((colonIndex = name.indexOf(';')) != -1) {
- params = new StringBuilder(name.substring(colonIndex));
- name = name.substring(0, colonIndex);
- }
-
- // No main / sub separator, assumes name/*.
- if ((slashIndex = name.indexOf('/')) == -1) {
- mainType = normalizeToken(name);
- subType = "*";
- } else {
- // Normalizes the main and sub types.
- mainType = normalizeToken(name.substring(0, slashIndex));
- subType = normalizeToken(name.substring(slashIndex + 1));
- }
-
- // Merge parameters taken from the name and the method argument.
- if (parameters != null && !parameters.isEmpty()) {
- try {
- 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);
- hw.appendParameterSeparator();
- hw.appendSpace();
- hw.append(p);
- }
- params.append(hw.toString());
- hw.close();
- } catch (IOException e) {
- Context.getCurrentLogger().log(Level.INFO, "Unable to parse the media type parameter", e);
- }
- }
-
- 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.
- * @param description The description.
- * @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);
- }
-
- /**
- * Returns the media type 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 media type.
- */
- public static MediaType valueOf(String name) {
- MediaType result = null;
-
- if (!StringUtils.isNullOrEmpty(name)) {
- result = getTypes().get(name);
- if (result == null) {
- result = new MediaType(name);
- }
- }
-
- return result;
- }
-
- /** The list of parameters. */
- private volatile Series parameters;
-
- /**
- * Constructor.
- *
- * @param name The name.
- */
- public MediaType(String name) {
- this(name, null, "Media type or range of media types");
- }
-
- /**
- * Constructor.
- *
- * @param name The name.
- * @param parameters The list of parameters.
- */
- public MediaType(String name, Series parameters) {
- this(name, parameters, "Media type or range of media types");
- }
-
- /**
- * Constructor.
- *
- * @param name The name.
- * @param parameters The list of parameters.
- * @param description The description.
- */
- @SuppressWarnings("unchecked")
- public MediaType(String name, Series parameters, String description) {
- super(normalizeType(name, parameters), description);
-
- if (parameters != null) {
- this.parameters = (Series) Series.unmodifiableSeries(parameters);
- }
- }
-
- /**
- * Constructor.
- *
- * @param name The name.
- * @param description The description.
- */
- public MediaType(String name, String description) {
- this(name, null, description);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean equals(Object obj) {
- return equals(obj, false);
- }
-
- /**
- * 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) {
- boolean result = (obj == this);
-
- // 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 (getMainType().equals(that.getMainType()) && getSubType().equals(that.getSubType())) {
- result = ignoreParameters || getParameters().equals(that.getParameters());
- }
- }
- }
-
- return result;
- }
-
- /**
- * Returns the main type.
- *
- * @return The main type.
- */
- public String getMainType() {
- String result = null;
-
- if (getName() != null) {
- int index = getName().indexOf('/');
-
- // Some clients appear to use name types without subtypes
- if (index == -1) {
- index = getName().indexOf(';');
- }
-
- if (index == -1) {
- result = getName();
- } else {
- result = getName().substring(0, index);
- }
- }
-
- return result;
- }
-
- /**
- * 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")
- public Series getParameters() {
- // Lazy initialization with double-check.
- Series p = this.parameters;
- if (p == null) {
- synchronized (this) {
- p = this.parameters;
- if (p == null) {
- Series params = null;
-
- if (getName() != null) {
- int index = getName().indexOf(';');
-
- if (index != -1) {
- params = new Form(getName().substring(index + 1).trim(), ';');
- }
- }
-
- if (params == null) {
- params = new Series(Parameter.class);
- }
-
- this.parameters = p = (Series) Series.unmodifiableSeries(params);
- }
- }
- }
- return p;
- }
-
- /**
- * {@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.
- */
- @Override
- public MediaType getParent() {
- MediaType result = null;
-
- if (getParameters().size() > 0) {
- result = MediaType.valueOf(getMainType() + "/" + getSubType());
- } else {
- if (getSubType().equals("*")) {
- result = equals(ALL) ? null : ALL;
- } else {
- result = MediaType.valueOf(getMainType() + "/*");
- }
- }
-
- return result;
- }
-
- /**
- * Returns the sub-type.
- *
- * @return The sub-type.
- */
- public String getSubType() {
- String result = null;
-
- if (getName() != null) {
- final int slash = getName().indexOf('/');
-
- if (slash == -1) {
- // No subtype found, assume that all subtypes are accepted
- result = "*";
- } else {
- final int separator = getName().indexOf(';');
- if (separator == -1) {
- result = getName().substring(slash + 1);
- } else {
- result = getName().substring(slash + 1, separator);
- }
- }
- }
-
- return result;
- }
-
- /** {@inheritDoc} */
- @Override
- public int hashCode() {
- return SystemUtils.hashCode(super.hashCode(), getParameters());
- }
-
- /**
- * 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)
- */
- @Override
- public boolean includes(Metadata included) {
- return includes(included, true);
- }
-
- /**
- * 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
- *
- *
- * @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)
- */
- public boolean includes(Metadata included, boolean ignoreParameters) {
- boolean result = equals(ALL) || equals(included);
-
- if (!result && (included instanceof MediaType)) {
- MediaType includedMediaType = (MediaType) included;
-
- if (getMainType().equals(includedMediaType.getMainType())) {
- // Both media types are different
- if (getSubType().equals(includedMediaType.getSubType())) {
- if (ignoreParameters) {
- result = true;
- } else {
- // Check parameters:
- // 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++) {
- Parameter param = getParameters().get(i);
- 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()));
- }
- }
- } else if (getSubType().equals("*")) {
- result = true;
- } else if (getSubType().startsWith("*+")
- && includedMediaType.getSubType().endsWith(getSubType().substring(2))) {
- result = true;
- }
- }
- }
-
- return result;
- }
-
- /**
- * 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 "*".
- *
- * @return True if this media type is concrete.
- */
- public boolean isConcrete() {
- return !getName().contains("*");
- }
+ 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:
+ *
+ * - "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
+ *
+ *
+ * @param mediaTypes An array of media types.
+ * @return The most concrete MediaType.
+ * @throws IllegalArgumentException If the array is null or empty.
+ */
+ public static MediaType getMostSpecific(MediaType... mediaTypes)
+ throws IllegalArgumentException {
+ if ((mediaTypes == null) || (mediaTypes.length == 0)) {
+ throw new IllegalArgumentException(
+ "You must give at least one MediaType");
+ }
+
+ if (mediaTypes.length == 1) {
+ return mediaTypes[0];
+ }
+
+ MediaType mostSpecific = mediaTypes[0];
+
+ for (int i = 1; i < mediaTypes.length; i++) {
+ MediaType mediaType = mediaTypes[i];
+
+ if (mediaType != null) {
+ if (mediaType.getMainType().equals("*")) {
+ continue;
+ }
+
+ if (mostSpecific.getMainType().equals("*")) {
+ mostSpecific = mediaType;
+ continue;
+ }
+
+ if (mostSpecific.getSubType().contains("*")) {
+ mostSpecific = mediaType;
+ continue;
+ }
+ }
+ }
+
+ 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.
+ *
+ * @param token Token to normalize.
+ * @return The normalized token.
+ * @throws IllegalArgumentException if token is not legal.
+ */
+ private static String normalizeToken(String token) {
+ int length;
+ char c;
+
+ // Makes sure we're not dealing with a "*" token.
+ token = token.trim();
+ if (token.isEmpty() || "*".equals(token))
+ return "*";
+
+ // Makes sure the token is RFC compliant.
+ length = token.length();
+ for (int i = 0; i < length; i++) {
+ c = token.charAt(i);
+ if (c <= 32 || c >= 127 || _TSPECIALS.indexOf(c) != -1)
+ throw new IllegalArgumentException("Illegal token: " + token);
+ }
+
+ return token;
+ }
+
+ /**
+ * Normalizes the specified media type.
+ *
+ * @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) {
+ int slashIndex;
+ int colonIndex;
+ String mainType;
+ String subType;
+ StringBuilder params = null;
+
+ // Ignore null names (backward compatibility).
+ if (name == null)
+ return null;
+
+ // Check presence of parameters
+ if ((colonIndex = name.indexOf(';')) != -1) {
+ params = new StringBuilder(name.substring(colonIndex));
+ name = name.substring(0, colonIndex);
+ }
+
+ // No main / sub separator, assumes name/*.
+ if ((slashIndex = name.indexOf('/')) == -1) {
+ mainType = normalizeToken(name);
+ subType = "*";
+ } else {
+ // Normalizes the main and sub types.
+ mainType = normalizeToken(name.substring(0, slashIndex));
+ subType = normalizeToken(name.substring(slashIndex + 1));
+ }
+
+ // Merge parameters taken from the name and the method argument.
+ if (parameters != null && !parameters.isEmpty()) {
+ try {
+ 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);
+ hw.appendParameterSeparator();
+ hw.appendSpace();
+ hw.append(p);
+ }
+ params.append(hw.toString());
+ hw.close();
+ } catch (IOException e) {
+ Context.getCurrentLogger().log(Level.INFO,
+ "Unable to parse the media type parameter", e);
+ }
+ }
+
+ 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.
+ * @param description The description.
+ * @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);
+ }
+
+ /**
+ * Returns the media type 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 media type.
+ */
+ public static MediaType valueOf(String name) {
+ MediaType result = null;
+
+ if (!StringUtils.isNullOrEmpty(name)) {
+ result = getTypes().get(name);
+ if (result == null) {
+ result = new MediaType(name);
+ }
+ }
+
+ return result;
+ }
+
+ /** The list of parameters. */
+ private volatile Series parameters;
+
+ /**
+ * 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 paramValue The value of the unique parameter to set.
+ */
+ public MediaType(MediaType original, String paramName, String paramValue) {
+ this(original, new Parameter(paramName, paramValue));
+ }
+
+ /**
+ * Constructor that clones an original media type.
+ *
+ * @param original The original media type to clone.
+ * @param parameter The unique parameter to set.
+ */
+ public MediaType(MediaType original, Parameter parameter) {
+ this(original, parameter == null ? null : parameter.createSeries());
+ }
+
+ /**
+ * 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,
+ (original == null) ? null : original.getDescription());
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param name The name.
+ */
+ public MediaType(String name) {
+ this(name, null, "Media type or range of media types");
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param name The name.
+ * @param parameters The list of parameters.
+ */
+ public MediaType(String name, Series parameters) {
+ this(name, parameters, "Media type or range of media types");
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param name The name.
+ * @param parameters The list of parameters.
+ * @param description The description.
+ */
+ @SuppressWarnings("unchecked")
+ public MediaType(String name, Series parameters,
+ String description) {
+ super(normalizeType(name, parameters), description);
+
+ if (parameters != null) {
+ this.parameters = (Series) Series
+ .unmodifiableSeries(parameters);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param name The name.
+ * @param description The description.
+ */
+ public MediaType(String name, String description) {
+ this(name, null, description);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ return equals(obj, false);
+ }
+
+ /**
+ * 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) {
+ boolean result = (obj == this);
+
+ // 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 (getMainType().equals(that.getMainType())
+ && getSubType().equals(that.getSubType())) {
+ result = ignoreParameters
+ || getParameters().equals(that.getParameters());
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the main type.
+ *
+ * @return The main type.
+ */
+ public String getMainType() {
+ String result = null;
+
+ if (getName() != null) {
+ int index = getName().indexOf('/');
+
+ // Some clients appear to use name types without subtypes
+ if (index == -1) {
+ index = getName().indexOf(';');
+ }
+
+ if (index == -1) {
+ result = getName();
+ } else {
+ result = getName().substring(0, index);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * 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")
+ public Series getParameters() {
+ // Lazy initialization with double-check.
+ Series p = this.parameters;
+ if (p == null) {
+ synchronized (this) {
+ p = this.parameters;
+ if (p == null) {
+ Series params = null;
+
+ if (getName() != null) {
+ int index = getName().indexOf(';');
+
+ if (index != -1) {
+ params = new Form(
+ getName().substring(index + 1).trim(), ';');
+ }
+ }
+
+ if (params == null) {
+ params = new Series(Parameter.class);
+ }
+
+ this.parameters = p = (Series) Series
+ .unmodifiableSeries(params);
+ }
+ }
+ }
+ return p;
+ }
+
+ /**
+ * {@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.
+ */
+ @Override
+ public MediaType getParent() {
+ MediaType result = null;
+
+ if (getParameters().size() > 0) {
+ result = MediaType.valueOf(getMainType() + "/" + getSubType());
+ } else {
+ if (getSubType().equals("*")) {
+ result = equals(ALL) ? null : ALL;
+ } else {
+ result = MediaType.valueOf(getMainType() + "/*");
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the sub-type.
+ *
+ * @return The sub-type.
+ */
+ public String getSubType() {
+ String result = null;
+
+ if (getName() != null) {
+ final int slash = getName().indexOf('/');
+
+ if (slash == -1) {
+ // No subtype found, assume that all subtypes are accepted
+ result = "*";
+ } else {
+ final int separator = getName().indexOf(';');
+ if (separator == -1) {
+ result = getName().substring(slash + 1);
+ } else {
+ result = getName().substring(slash + 1, separator);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return SystemUtils.hashCode(super.hashCode(), getParameters());
+ }
+
+ /**
+ * 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)
+ */
+ @Override
+ public boolean includes(Metadata included) {
+ return includes(included, true);
+ }
+
+ /**
+ * 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
+ *
+ *
+ * @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)
+ */
+ public boolean includes(Metadata included, boolean ignoreParameters) {
+ boolean result = equals(ALL) || equals(included);
+
+ if (!result && (included instanceof MediaType)) {
+ MediaType includedMediaType = (MediaType) included;
+
+ if (getMainType().equals(includedMediaType.getMainType())) {
+ // Both media types are different
+ if (getSubType().equals(includedMediaType.getSubType())) {
+ if (ignoreParameters) {
+ result = true;
+ } else {
+ // Check parameters:
+ // 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++) {
+ Parameter param = getParameters().get(i);
+ 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()));
+ }
+ }
+ } else if (getSubType().equals("*")) {
+ result = true;
+ } else if (getSubType().startsWith("*+") && includedMediaType
+ .getSubType().endsWith(getSubType().substring(2))) {
+ result = true;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * 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 "*".
+ *
+ * @return True if this media type is concrete.
+ */
+ public boolean isConcrete() {
+ return !getName().contains("*");
+ }
}
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Parameter.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Parameter.java
index dcf616e011..743c35942f 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Parameter.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Parameter.java
@@ -11,6 +11,7 @@
import org.restlet.engine.util.SystemUtils;
import org.restlet.util.NamedValue;
+import org.restlet.util.Series;
import java.io.IOException;
import java.util.Objects;
@@ -29,6 +30,19 @@ public class Parameter implements Comparable, NamedValue {
/** 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.
+ */
+ public Series createSeries() {
+ Series result = new Form();
+ result.add(this);
+ return result;
+ }
+
/**
* Creates a parameter.
*
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java
index aa0932e138..8d8737fec5 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java
@@ -11,6 +11,7 @@
import org.restlet.data.CharacterSet;
import org.restlet.data.MediaType;
+import org.restlet.data.Parameter;
import org.restlet.representation.Representation;
import java.io.IOException;
@@ -50,15 +51,29 @@ public static MediaType readMediaType(String contentType) {
* @return The HTTP "Content-Type" header.
*/
public static String writeHeader(MediaType mediaType, CharacterSet characterSet) {
- String result = mediaType.toString();
+ 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();
}
+ */
- return result;
-
+ 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);
+ } else {
+ result.append("; ").append(param.getName()).append("=").append(param.getValue());
+ }
+ }
+
+ return result.toString();
}
/**
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java
index 74466ab480..4a884e54ca 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java
@@ -22,6 +22,7 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
+import java.util.Objects;
import java.util.logging.Level;
/**
@@ -41,12 +42,23 @@ public HttpBasicHelper() {
@Override
public void formatRequest(ChallengeWriter cw, ChallengeRequest challenge, Response response,
Series httpHeaders) throws IOException {
- if (challenge.getRealm() != null) {
- cw.appendQuotedChallengeParameter("realm", challenge.getRealm());
+ 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
@@ -56,12 +68,25 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, Requ
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(), "ISO-8859-1")));
+ cw.append(Base64.getEncoder().encodeToString(IoUtils.toByteArray(credentials.toCharArray(), charset)));
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported encoding, unable to encode credentials");
@@ -105,14 +130,29 @@ public void parseRequest(ChallengeRequest challenge, Response response, Series httpHeaders) {
- try {
- byte[] credentialsEncoded = Base64.getDecoder().decode(challenge.getRawValue());
+ if (challenge.getRawValue() == null) {
+ getLogger().info("Cannot decode credentials: " + challenge.getRawValue());
+ return;
+ }
- if (credentialsEncoded == null) {
- getLogger().info("Cannot decode credentials: " + challenge.getRawValue());
+ 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 credentials = new String(credentialsEncoded, "ISO-8859-1");
+ byte[] credentialsEncoded = Base64.getDecoder().decode(challenge.getRawValue());
+
+ String credentials = new String(credentialsEncoded, charset);
int separator = credentials.indexOf(':');
if (separator == -1) {
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java
index 275d81a463..273d81cf9d 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java
@@ -11,10 +11,8 @@
import org.junit.jupiter.api.Test;
import org.restlet.engine.Engine;
-import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.ClientResource;
-import org.restlet.resource.ResourceException;
import java.io.File;
import java.io.IOException;
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java
index f29848cfef..4e71bb98dd 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java
@@ -11,8 +11,6 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.restlet.*;
@@ -24,7 +22,6 @@
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
/**
* Unit test case for the RIAP Internal routing protocol.
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java
index 7e622ffd32..c116b96517 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java
@@ -9,18 +9,15 @@
package org.restlet.engine.header;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.restlet.data.ClientInfo;
import org.restlet.data.Encoding;
import org.restlet.data.Header;
import org.restlet.data.MediaType;
import org.restlet.engine.util.DateUtils;
-import org.restlet.representation.Representation;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Base64;
import java.util.Date;
import java.util.List;
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java
index f1003fd7dc..e62f890faf 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java
@@ -14,7 +14,6 @@
import org.restlet.resource.Directory;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java
index c4b0a7725f..3badf2983b 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java
@@ -13,7 +13,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.restlet.engine.Engine;
import org.restlet.representation.ObjectRepresentation;
-import org.restlet.resource.ClientResource;
/**
* Test the annotated resources, client and server sides.
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java
index b859f17e04..4035a8512c 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java
@@ -9,9 +9,6 @@
package org.restlet.resource;
-import org.restlet.resource.Post;
-import org.restlet.resource.ServerResource;
-
public abstract class AbstractGenericAnnotatedServerResource extends ServerResource {
@Post
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException01.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException01.java
index f9e79d5749..620bf91493 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException01.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException01.java
@@ -11,8 +11,6 @@
import java.util.Date;
-import org.restlet.resource.Status;
-
@Status(value = 400, serialize = false)
public class MyException01 extends Throwable {
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException02.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException02.java
index 8b431b9b3b..5ff4e2055d 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException02.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyException02.java
@@ -9,8 +9,6 @@
package org.restlet.resource;
-import org.restlet.resource.Status;
-
@Status(value = 400)
public class MyException02 extends Throwable {
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource02.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource02.java
index edeef7f103..786b164cb4 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource02.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource02.java
@@ -12,8 +12,6 @@
import org.restlet.data.MediaType;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
-import org.restlet.resource.Get;
-import org.restlet.resource.ServerResource;
public class MyResource02 extends ServerResource {
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource04.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource04.java
index cadd4de7ec..2c0240b31f 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource04.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource04.java
@@ -9,9 +9,6 @@
package org.restlet.resource;
-import org.restlet.resource.Get;
-import org.restlet.resource.ServerResource;
-
public class MyResource04 extends ServerResource {
@Get("xml")
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource05.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource05.java
index d87341832b..a036ccef83 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource05.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource05.java
@@ -9,9 +9,6 @@
package org.restlet.resource;
-import org.restlet.resource.Post;
-import org.restlet.resource.ServerResource;
-
public class MyResource05 extends ServerResource {
@Post("txt:xml")
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource06.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource06.java
index c777fc8f79..206681092a 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource06.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource06.java
@@ -11,9 +11,6 @@
import java.io.IOException;
-import org.restlet.resource.Post;
-import org.restlet.resource.ServerResource;
-
public class MyResource06 extends ServerResource {
@Post("txt:xml")
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource07.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource07.java
index c927c7114b..f38a0f867a 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource07.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource07.java
@@ -9,9 +9,6 @@
package org.restlet.resource;
-import org.restlet.resource.Post;
-import org.restlet.resource.ServerResource;
-
public class MyResource07 extends ServerResource {
@Post("json:xml")
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource08.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource08.java
index 350d0d729d..cb0eb69e41 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource08.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource08.java
@@ -9,9 +9,6 @@
package org.restlet.resource;
-import org.restlet.resource.Post;
-import org.restlet.resource.ServerResource;
-
public class MyResource08 extends ServerResource {
@Post("xml|json:xml|json")
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource12.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource12.java
index 8d8574a1b4..9ab5705ec1 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource12.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource12.java
@@ -10,8 +10,6 @@
package org.restlet.resource;
import org.restlet.data.Form;
-import org.restlet.resource.Get;
-import org.restlet.resource.Put;
/**
* Sample annotated interface.
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource17.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource17.java
index 85381289bb..2294da3ab3 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource17.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/resource/MyResource17.java
@@ -9,8 +9,6 @@
package org.restlet.resource;
-import org.restlet.resource.Post;
-
public interface MyResource17 {
@Post
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java
index 8c723b9cbb..e88b04ce5c 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java
@@ -9,7 +9,6 @@
package org.restlet.routing;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.restlet.Request;
import org.restlet.Response;
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java
index 6d886907c4..328c2ab542 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java
@@ -9,15 +9,32 @@
package org.restlet.security;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.restlet.*;
-import org.restlet.data.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
import java.util.Arrays;
+import java.util.stream.Stream;
-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.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.MethodSource;
+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.ChallengeResponse;
+import org.restlet.data.ChallengeScheme;
+import org.restlet.data.MediaType;
+import org.restlet.data.Method;
+import org.restlet.data.Protocol;
+import org.restlet.data.Status;
/**
* Restlet unit tests for HTTP Basic authentication client/server.
@@ -27,244 +44,145 @@
*/
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 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";
-
- private ChallengeAuthenticator authenticator;
-
- private Component component;
-
- private String uri;
-
- private MapVerifier verifier;
-
- @Test
- public void guardLong() {
- assertEquals(
- Verifier.RESULT_VALID,
- this.verifier.verify(LONG_USERNAME, LONG_PASSWORD.toCharArray()),
- "Didn't authenticate short user/pwd"
- );
- }
-
- @Test
- public void guardLongWrong() {
- assertEquals(
- Verifier.RESULT_INVALID,
- this.verifier.verify(LONG_USERNAME, SHORT_PASSWORD.toCharArray()),
- "Authenticated long username with wrong password"
- );
- }
-
- // Test our guard.checkSecret() stand-alone
- @Test
- public void guardShort() {
- assertEquals(
- Verifier.RESULT_VALID,
- this.verifier.verify(SHORT_USERNAME, SHORT_PASSWORD.toCharArray()),
- "Didn't authenticate short user/pwd"
- );
- }
-
- @Test
- public void guardShortWrong() {
- assertEquals(
- Verifier.RESULT_INVALID,
- this.verifier.verify(SHORT_USERNAME, LONG_PASSWORD.toCharArray()),
- "Authenticated short username with wrong password"
- );
- }
-
- @Test
- public void guardWrongUser() {
- assertEquals(
- Verifier.RESULT_INVALID,
- this.verifier.verify(WRONG_USERNAME, SHORT_PASSWORD.toCharArray()),
- "Authenticated wrong username"
- );
- }
-
- public void HttpBasicLong() throws Exception {
- Request request = new Request(Method.GET, this.uri);
- Client client = new Client(Protocol.HTTP);
-
- ChallengeResponse authentication = new ChallengeResponse(
- ChallengeScheme.HTTP_BASIC, LONG_USERNAME, LONG_PASSWORD);
- request.setChallengeResponse(authentication);
-
- final Response response = client.handle(request);
- assertEquals(
- Status.SUCCESS_OK, response.getStatus(),
- "Long username did not return 200 OK"
- );
- assertEquals(AUTHENTICATED_MSG, response.getEntity().getText());
-
- client.stop();
- }
-
- public void HttpBasicLongWrong() throws Exception {
- final Request request = new Request(Method.GET, this.uri);
- final Client client = new Client(Protocol.HTTP);
-
- final ChallengeResponse authentication = new ChallengeResponse(
- ChallengeScheme.HTTP_BASIC, LONG_USERNAME, SHORT_PASSWORD);
- request.setChallengeResponse(authentication);
-
- final Response response = client.handle(request);
-
- assertEquals(
- Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus(),
- "Long username w/wrong pw did not throw 403"
- );
-
- client.stop();
- }
-
- // Test various HTTP Basic auth connections
- public void HttpBasicNone() throws Exception {
- final Request request = new Request(Method.GET, this.uri);
- final Client client = new Client(Protocol.HTTP);
- final Response response = client.handle(request);
- assertEquals(
- Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus(),
- "No user did not throw 401"
- );
- client.stop();
- }
-
- public void HttpBasicShort() throws Exception {
- final Request request = new Request(Method.GET, this.uri);
- final Client client = new Client(Protocol.HTTP);
-
- final ChallengeResponse authentication = new ChallengeResponse(
- ChallengeScheme.HTTP_BASIC, SHORT_USERNAME, SHORT_PASSWORD);
- request.setChallengeResponse(authentication);
-
- final Response response = client.handle(request);
- assertEquals(
- Status.SUCCESS_OK, response.getStatus(),
- "Short username did not return 200 OK"
- );
- assertEquals(AUTHENTICATED_MSG, response.getEntity().getText());
-
- client.stop();
- }
-
- public void HttpBasicShortWrong() throws Exception {
- final Request request = new Request(Method.GET, this.uri);
- final Client client = new Client(Protocol.HTTP);
-
- final ChallengeResponse authentication = new ChallengeResponse(
- ChallengeScheme.HTTP_BASIC, SHORT_USERNAME, LONG_PASSWORD);
- request.setChallengeResponse(authentication);
-
- final Response response = client.handle(request);
-
- assertEquals(
- Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus(),
- "Short username did not throw 401"
- );
-
- client.stop();
- }
-
- public void HttpBasicWrongUser() throws Exception {
- final Request request = new Request(Method.GET, this.uri);
- final Client client = new Client(Protocol.HTTP);
-
- final ChallengeResponse authentication = new ChallengeResponse(
- ChallengeScheme.HTTP_BASIC, WRONG_USERNAME, SHORT_PASSWORD);
- request.setChallengeResponse(authentication);
-
- final Response response = client.handle(request);
-
- assertEquals(
- Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus(),
- "Wrong username did not throw 401"
- );
-
- client.stop();
- }
-
- @BeforeEach
- public void makeServer() throws Exception {
- this.component = new Component();
- final Server server = this.component.getServers().add(Protocol.HTTP, 0);
-
- final Application application = new Application() {
- @Override
- public Restlet createInboundRoot() {
- HttpBasicTestCase.this.verifier = new TestVerifier();
- HttpBasicTestCase.this.authenticator = new ChallengeAuthenticator(
- getContext(), ChallengeScheme.HTTP_BASIC,
- HttpBasicTestCase.class.getSimpleName());
- HttpBasicTestCase.this.authenticator
- .setVerifier(HttpBasicTestCase.this.verifier);
- HttpBasicTestCase.this.authenticator
- .setNext(new AuthenticatedRestlet());
- return HttpBasicTestCase.this.authenticator;
- }
- };
-
- this.component.getDefaultHost().attach(application);
- this.component.start();
- this.uri = "http://localhost:" + server.getActualPort() + "/";
- }
-
- @AfterEach
- public void stopServer() throws Exception {
- if (this.component.isStarted()) {
- this.component.stop();
- }
- this.component = null;
- }
-
- @Test
- public void testHttpBasic() throws Exception {
- HttpBasicWrongUser();
- HttpBasicShort();
- HttpBasicShortWrong();
- HttpBasicNone();
- HttpBasicLong();
- HttpBasicLongWrong();
- }
+ 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.java/org.restlet/src/test/java/org/restlet/security/SaasApplication.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/security/SaasApplication.java
index 94468f2440..75c7fcd094 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/security/SaasApplication.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/security/SaasApplication.java
@@ -14,7 +14,6 @@
import org.restlet.Restlet;
import org.restlet.data.ChallengeScheme;
import org.restlet.routing.Router;
-import org.restlet.security.*;
/**
* Sample SAAS application with a Basic authenticator guarding a hello world
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java
index 21baf2661f..ca3e83df39 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java
@@ -12,7 +12,6 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.restlet.Component;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.Status;
import org.restlet.engine.Engine;