Extract common HTTP classes to common module.

Update version to 0.10.0 due to backward incompatible changes
This commit is contained in:
Tomas Langer
2018-09-11 13:39:56 +02:00
committed by Tomas Langer
parent df28d67d68
commit c3ddab97fe
307 changed files with 3476 additions and 3098 deletions

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon</groupId>
<artifactId>helidon-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-bom</artifactId>
<packaging>pom</packaging>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-common</artifactId>
<name>Helidon Common</name>

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* Utility methods to help with loading of java services (mostly SPI related).
*/
public final class SpiHelper {
private SpiHelper() {
}
/**
* Loads the first service implementation or throw an exception if nothing found.
*
* @param service the service class to load
* @param <T> service type
* @return the loaded service
* @throws IllegalStateException if none implementation found
*/
public static <T> T loadSpi(Class<T> service) {
ServiceLoader<T> servers = ServiceLoader.load(service);
Iterator<T> serversIt = servers.iterator();
if (serversIt.hasNext()) {
return serversIt.next();
} else {
throw new IllegalStateException("No implementation found for SPI: " + service.getName());
}
}
}

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<name>Helidon Common Configurable</name>
<artifactId>helidon-common-configurable</artifactId>

54
common/http/pom.xml Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>helidon-common-project</artifactId>
<groupId>io.helidon.common</groupId>
<version>0.10.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helidon-common-http</artifactId>
<dependencies>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-reactive</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.function.Predicate;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,11 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
/**
* Signals that a method has been invoked on already completed {@link ServerResponse} or {@link ResponseHeaders}.
* Signals that a mutation method has been invoked on a resource that is already completed.
*
* It is no longer possible to mute state of these objects.
*/
public class AlreadyCompletedException extends IllegalStateException {
@@ -34,9 +35,9 @@ public class AlreadyCompletedException extends IllegalStateException {
/**
* Constructs an {@link AlreadyCompletedException} with the specified detail message and cause.
*
* @param message the detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method).
* (A {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.)
* @param message the detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method).
* (A {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.)
*/
public AlreadyCompletedException(String message, Throwable cause) {
super(message, cause);
@@ -45,8 +46,8 @@ public class AlreadyCompletedException extends IllegalStateException {
/**
* Constructs an {@link AlreadyCompletedException} with the specified cause.
*
* @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method).
* (A {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.)
* @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method).
* (A {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.)
*/
public AlreadyCompletedException(Throwable cause) {
super(cause);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
/**
* Extracted from Guava.
@@ -85,7 +85,6 @@ final class Ascii {
return isUpperCase(c) ? (char) (c ^ 0x20) : c;
}
/**
* Indicates whether {@code c} is one of the twenty-six lowercase ASCII alphabetic characters
* between {@code 'a'} and {@code 'z'} inclusive. All others (including non-ASCII characters)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,12 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Objects;
/**
* Extracted from Guava.
* <p>
@@ -45,7 +44,257 @@ import java.util.Objects;
@SuppressWarnings({"checkstyle:VisibilityModifier", "checkstyle:RedundantModifier"})
abstract class CharMatcher {
/** Implementation of {@link #ascii()}. */
/**
* Constructor for use by subclasses. When subclassing, you may want to override
* {@code toString()} to provide a useful description.
*/
protected CharMatcher() {
}
/**
* Determines whether a character is ASCII, meaning that its code point is less than 128.
*
* @since 19.0 (since 1.0 as constant {@code ASCII})
*/
public static CharMatcher ascii() {
return Ascii.INSTANCE;
}
/**
* Returns a {@code char} matcher that matches any character except the one specified.
*
* <p>To negate another {@code CharMatcher}, use {@link #negate()}.
*/
public static CharMatcher isNot(final char match) {
return new IsNot(match);
}
/**
* Matches any character.
*
* @since 19.0 (since 1.0 as constant {@code ANY})
*/
public static CharMatcher any() {
return Any.INSTANCE;
}
/**
* Matches no characters.
*
* @since 19.0 (since 1.0 as constant {@code NONE})
*/
public static CharMatcher none() {
return None.INSTANCE;
}
/**
* Determines whether a character is an ISO control character as specified by
* {@link Character#isISOControl(char)}.
*
* @since 19.0 (since 1.0 as constant {@code JAVA_ISO_CONTROL})
*/
public static CharMatcher javaIsoControl() {
return JavaIsoControl.INSTANCE;
}
/**
* Returns a {@code char} matcher that matches only one specified character.
*/
public static CharMatcher is(final char match) {
return new Is(match);
}
private static CharMatcher.IsEither isEither(char c1, char c2) {
return new CharMatcher.IsEither(c1, c2);
}
/**
* Returns a {@code char} matcher that matches any character not present in the given character
* sequence.
*/
public static CharMatcher noneOf(CharSequence sequence) {
return anyOf(sequence).negate();
}
/**
* Returns a {@code char} matcher that matches any character present in the given character
* sequence.
*/
public static CharMatcher anyOf(final CharSequence sequence) {
switch (sequence.length()) {
case 0:
return none();
case 1:
return is(sequence.charAt(0));
case 2:
return isEither(sequence.charAt(0), sequence.charAt(1));
default:
// TODO(lowasser): is it potentially worth just going ahead and building a precomputed
// matcher?
return new AnyOf(sequence);
}
}
/**
* Returns the Java Unicode escape sequence for the given character, in the form "\u12AB" where
* "12AB" is the four hexadecimal digits representing the 16 bits of the UTF-16 character.
*/
private static String showCharacter(char c) {
String hex = "0123456789ABCDEF";
char[] tmp = {'\\', 'u', '\0', '\0', '\0', '\0'};
for (int i = 0; i < 4; i++) {
tmp[5 - i] = hex.charAt(c & 0xF);
c = (char) (c >> 4);
}
return String.copyValueOf(tmp);
}
/**
* Determines a true or false value for the given character.
*/
public abstract boolean matches(char c);
/**
* Returns a matcher that matches any character not matched by this matcher.
*/
public CharMatcher negate() {
return new Negated(this);
}
/**
* Returns a matcher that matches any character matched by both this matcher and {@code other}.
*/
public CharMatcher and(CharMatcher other) {
return new And(this, other);
}
/**
* Returns a matcher that matches any character matched by either this matcher or {@code other}.
*/
public CharMatcher or(CharMatcher other) {
return new Or(this, other);
}
// Abstract methods
/**
* Sets bits in {@code table} matched by this matcher.
*/
void setBits(BitSet table) {
for (int c = Character.MAX_VALUE; c >= Character.MIN_VALUE; c--) {
if (matches((char) c)) {
table.set(c);
}
}
}
// Non-static factories
/**
* Returns {@code true} if a character sequence contains at least one matching character.
* Equivalent to {@code !matchesNoneOf(sequence)}.
*
* <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
* character, until this returns {@code true} or the end is reached.
*
* @param sequence the character sequence to examine, possibly empty
* @return {@code true} if this matcher matches at least one character in the sequence
* @since 8.0
*/
public boolean matchesAnyOf(CharSequence sequence) {
return !matchesNoneOf(sequence);
}
/**
* Returns {@code true} if a character sequence contains only matching characters.
*
* <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
* character, until this returns {@code false} or the end is reached.
*
* @param sequence the character sequence to examine, possibly empty
* @return {@code true} if this matcher matches every character in the sequence, including when
* the sequence is empty
*/
public boolean matchesAllOf(CharSequence sequence) {
for (int i = sequence.length() - 1; i >= 0; i--) {
if (!matches(sequence.charAt(i))) {
return false;
}
}
return true;
}
/**
* Returns {@code true} if a character sequence contains no matching characters. Equivalent to
* {@code !matchesAnyOf(sequence)}.
*
* <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
* character, until this returns {@code true} or the end is reached.
*
* @param sequence the character sequence to examine, possibly empty
* @return {@code true} if this matcher matches no characters in the sequence, including when
* the sequence is empty
*/
public boolean matchesNoneOf(CharSequence sequence) {
return indexIn(sequence) == -1;
}
/**
* Returns the index of the first matching character in a character sequence, or {@code -1} if no
* matching character is present.
*
* <p>The default implementation iterates over the sequence in forward order calling
* {@link #matches} for each character.
*
* @param sequence the character sequence to examine from the beginning
* @return an index, or {@code -1} if no character matches
*/
public int indexIn(CharSequence sequence) {
return indexIn(sequence, 0);
}
/**
* Returns the index of the first matching character in a character sequence, starting from a
* given position, or {@code -1} if no character matches after that position.
*
* <p>The default implementation iterates over the sequence in forward order, beginning at {@code
* start}, calling {@link #matches} for each character.
*
* @param sequence the character sequence to examine
* @param start the first index to examine; must be nonnegative and no greater than {@code
* sequence.length()}
* @return the index of the first matching character, guaranteed to be no less than {@code start},
* or {@code -1} if no character matches
* @throws IndexOutOfBoundsException if start is negative or greater than {@code
* sequence.length()}
*/
public int indexIn(CharSequence sequence, int start) {
int length = sequence.length();
Preconditions.checkPositionIndex(start, length);
for (int i = start; i < length; i++) {
if (matches(sequence.charAt(i))) {
return i;
}
}
return -1;
}
/**
* Returns the number of matching characters found in a character sequence.
*/
public int countIn(CharSequence sequence) {
int count = 0;
for (int i = 0; i < sequence.length(); i++) {
if (matches(sequence.charAt(i))) {
count++;
}
}
return count;
}
/**
* Implementation of {@link #ascii()}.
*/
private static final class Ascii extends NamedFastMatcher {
static final Ascii INSTANCE = new Ascii();
@@ -60,7 +309,9 @@ abstract class CharMatcher {
}
}
/** {@link FastMatcher} which overrides {@code toString()} with a custom name. */
/**
* {@link FastMatcher} which overrides {@code toString()} with a custom name.
*/
abstract static class NamedFastMatcher extends FastMatcher {
private final String description;
@@ -75,13 +326,15 @@ abstract class CharMatcher {
}
}
/** A matcher for which precomputation will not yield any significant benefit. */
/**
* A matcher for which precomputation will not yield any significant benefit.
*/
abstract static class FastMatcher extends CharMatcher {
// @Override
// public final CharMatcher precomputed() {
// return this;
// }
// @Override
// public final CharMatcher precomputed() {
// return this;
// }
@Override
public CharMatcher negate() {
@@ -89,20 +342,24 @@ abstract class CharMatcher {
}
}
/** Negation of a {@link FastMatcher}. */
/**
* Negation of a {@link FastMatcher}.
*/
static class NegatedFastMatcher extends Negated {
NegatedFastMatcher(CharMatcher original) {
super(original);
}
// @Override
// public final CharMatcher precomputed() {
// return this;
// }
// @Override
// public final CharMatcher precomputed() {
// return this;
// }
}
/** Implementation of {@link #javaIsoControl()}. */
/**
* Implementation of {@link #javaIsoControl()}.
*/
private static final class JavaIsoControl extends NamedFastMatcher {
static final JavaIsoControl INSTANCE = new JavaIsoControl();
@@ -117,8 +374,11 @@ abstract class CharMatcher {
}
}
// Text processing routines
/** Implementation of {@link #negate()}. */
/**
* Implementation of {@link #negate()}.
*/
private static class Negated extends CharMatcher {
final CharMatcher original;
@@ -166,8 +426,9 @@ abstract class CharMatcher {
}
}
/** Implementation of {@link #and(CharMatcher)}. */
/**
* Implementation of {@link #and(CharMatcher)}.
*/
private static final class And extends CharMatcher {
final CharMatcher first;
@@ -199,7 +460,9 @@ abstract class CharMatcher {
}
}
/** Implementation of {@link #or(CharMatcher)}. */
/**
* Implementation of {@link #or(CharMatcher)}.
*/
private static final class Or extends CharMatcher {
final CharMatcher first;
@@ -227,7 +490,9 @@ abstract class CharMatcher {
}
}
/** Implementation of {@link #isNot(char)}. */
/**
* Implementation of {@link #isNot(char)}.
*/
private static final class IsNot extends FastMatcher {
private final char match;
@@ -268,8 +533,9 @@ abstract class CharMatcher {
}
}
/** Implementation of {@link #anyOf(CharSequence)} for three or more characters. */
/**
* Implementation of {@link #anyOf(CharSequence)} for three or more characters.
*/
private static final class AnyOf extends CharMatcher {
private final char[] chars;
@@ -302,7 +568,9 @@ abstract class CharMatcher {
}
}
/** Implementation of {@link #is(char)}. */
/**
* Implementation of {@link #is(char)}.
*/
private static final class Is extends FastMatcher {
private final char match;
@@ -316,10 +584,10 @@ abstract class CharMatcher {
return c == match;
}
// @Override
// public String replaceFrom(CharSequence sequence, char replacement) {
// return sequence.toString().replace(match, replacement);
// }
// @Override
// public String replaceFrom(CharSequence sequence, char replacement) {
// return sequence.toString().replace(match, replacement);
// }
@Override
public CharMatcher and(CharMatcher other) {
@@ -347,7 +615,9 @@ abstract class CharMatcher {
}
}
/** Implementation of {@link #any()}. */
/**
* Implementation of {@link #any()}.
*/
private static final class Any extends NamedFastMatcher {
static final Any INSTANCE = new Any();
@@ -373,10 +643,10 @@ abstract class CharMatcher {
return (start == length) ? -1 : start;
}
// @Override
// public int lastIndexIn(CharSequence sequence) {
// return sequence.length() - 1;
// }
// @Override
// public int lastIndexIn(CharSequence sequence) {
// return sequence.length() - 1;
// }
@Override
public boolean matchesAllOf(CharSequence sequence) {
@@ -389,38 +659,38 @@ abstract class CharMatcher {
return sequence.length() == 0;
}
// @Override
// public String removeFrom(CharSequence sequence) {
// Objects.requireNonNull(sequence);
// return "";
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, char replacement) {
// char[] array = new char[sequence.length()];
// Arrays.fill(array, replacement);
// return new String(array);
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, CharSequence replacement) {
// StringBuilder result = new StringBuilder(sequence.length() * replacement.length());
// for (int i = 0; i < sequence.length(); i++) {
// result.append(replacement);
// }
// return result.toString();
// }
//
// @Override
// public String collapseFrom(CharSequence sequence, char replacement) {
// return (sequence.length() == 0) ? "" : String.valueOf(replacement);
// }
//
// @Override
// public String trimFrom(CharSequence sequence) {
// Objects.requireNonNull(sequence);
// return "";
// }
// @Override
// public String removeFrom(CharSequence sequence) {
// Objects.requireNonNull(sequence);
// return "";
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, char replacement) {
// char[] array = new char[sequence.length()];
// Arrays.fill(array, replacement);
// return new String(array);
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, CharSequence replacement) {
// StringBuilder result = new StringBuilder(sequence.length() * replacement.length());
// for (int i = 0; i < sequence.length(); i++) {
// result.append(replacement);
// }
// return result.toString();
// }
//
// @Override
// public String collapseFrom(CharSequence sequence, char replacement) {
// return (sequence.length() == 0) ? "" : String.valueOf(replacement);
// }
//
// @Override
// public String trimFrom(CharSequence sequence) {
// Objects.requireNonNull(sequence);
// return "";
// }
@Override
public int countIn(CharSequence sequence) {
@@ -444,8 +714,9 @@ abstract class CharMatcher {
}
}
/** Implementation of {@link #none()}. */
/**
* Implementation of {@link #none()}.
*/
private static final class None extends NamedFastMatcher {
static final None INSTANCE = new None();
@@ -472,11 +743,11 @@ abstract class CharMatcher {
return -1;
}
// @Override
// public int lastIndexIn(CharSequence sequence) {
// Objects.requireNonNull(sequence);
// return -1;
// }
// @Override
// public int lastIndexIn(CharSequence sequence) {
// Objects.requireNonNull(sequence);
// return -1;
// }
@Override
public boolean matchesAllOf(CharSequence sequence) {
@@ -489,41 +760,41 @@ abstract class CharMatcher {
return true;
}
// @Override
// public String removeFrom(CharSequence sequence) {
// return sequence.toString();
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, char replacement) {
// return sequence.toString();
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, CharSequence replacement) {
// Objects.requireNonNull(replacement);
// return sequence.toString();
// }
//
// @Override
// public String collapseFrom(CharSequence sequence, char replacement) {
// return sequence.toString();
// }
//
// @Override
// public String trimFrom(CharSequence sequence) {
// return sequence.toString();
// }
//
// @Override
// public String trimLeadingFrom(CharSequence sequence) {
// return sequence.toString();
// }
//
// @Override
// public String trimTrailingFrom(CharSequence sequence) {
// return sequence.toString();
// }
// @Override
// public String removeFrom(CharSequence sequence) {
// return sequence.toString();
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, char replacement) {
// return sequence.toString();
// }
//
// @Override
// public String replaceFrom(CharSequence sequence, CharSequence replacement) {
// Objects.requireNonNull(replacement);
// return sequence.toString();
// }
//
// @Override
// public String collapseFrom(CharSequence sequence, char replacement) {
// return sequence.toString();
// }
//
// @Override
// public String trimFrom(CharSequence sequence) {
// return sequence.toString();
// }
//
// @Override
// public String trimLeadingFrom(CharSequence sequence) {
// return sequence.toString();
// }
//
// @Override
// public String trimTrailingFrom(CharSequence sequence) {
// return sequence.toString();
// }
@Override
public int countIn(CharSequence sequence) {
@@ -549,100 +820,8 @@ abstract class CharMatcher {
}
/**
* Determines whether a character is ASCII, meaning that its code point is less than 128.
*
* @since 19.0 (since 1.0 as constant {@code ASCII})
* Implementation of {@link #anyOf(CharSequence)} for exactly two characters.
*/
public static CharMatcher ascii() {
return Ascii.INSTANCE;
}
/**
* Constructor for use by subclasses. When subclassing, you may want to override
* {@code toString()} to provide a useful description.
*/
protected CharMatcher() {}
// Abstract methods
/** Determines a true or false value for the given character. */
public abstract boolean matches(char c);
// Non-static factories
/**
* Returns a matcher that matches any character not matched by this matcher.
*/
public CharMatcher negate() {
return new Negated(this);
}
/**
* Returns a matcher that matches any character matched by both this matcher and {@code other}.
*/
public CharMatcher and(CharMatcher other) {
return new And(this, other);
}
/**
* Returns a matcher that matches any character matched by either this matcher or {@code other}.
*/
public CharMatcher or(CharMatcher other) {
return new Or(this, other);
}
/**
* Returns a {@code char} matcher that matches any character except the one specified.
*
* <p>To negate another {@code CharMatcher}, use {@link #negate()}.
*/
public static CharMatcher isNot(final char match) {
return new IsNot(match);
}
/**
* Matches any character.
*
* @since 19.0 (since 1.0 as constant {@code ANY})
*/
public static CharMatcher any() {
return Any.INSTANCE;
}
/**
* Matches no characters.
*
* @since 19.0 (since 1.0 as constant {@code NONE})
*/
public static CharMatcher none() {
return None.INSTANCE;
}
/**
* Determines whether a character is an ISO control character as specified by
* {@link Character#isISOControl(char)}.
*
* @since 19.0 (since 1.0 as constant {@code JAVA_ISO_CONTROL})
*/
public static CharMatcher javaIsoControl() {
return JavaIsoControl.INSTANCE;
}
/**
* Returns a {@code char} matcher that matches only one specified character.
*/
public static CharMatcher is(final char match) {
return new Is(match);
}
private static CharMatcher.IsEither isEither(char c1, char c2) {
return new CharMatcher.IsEither(c1, c2);
}
/** Implementation of {@link #anyOf(CharSequence)} for exactly two characters. */
private static final class IsEither extends FastMatcher {
private final char match1;
@@ -669,167 +848,4 @@ abstract class CharMatcher {
return "CharMatcher.anyOf(\"" + showCharacter(match1) + showCharacter(match2) + "\")";
}
}
/**
* Sets bits in {@code table} matched by this matcher.
*/
void setBits(BitSet table) {
for (int c = Character.MAX_VALUE; c >= Character.MIN_VALUE; c--) {
if (matches((char) c)) {
table.set(c);
}
}
}
// Text processing routines
/**
* Returns {@code true} if a character sequence contains at least one matching character.
* Equivalent to {@code !matchesNoneOf(sequence)}.
*
* <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
* character, until this returns {@code true} or the end is reached.
*
* @param sequence the character sequence to examine, possibly empty
* @return {@code true} if this matcher matches at least one character in the sequence
* @since 8.0
*/
public boolean matchesAnyOf(CharSequence sequence) {
return !matchesNoneOf(sequence);
}
/**
* Returns {@code true} if a character sequence contains only matching characters.
*
* <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
* character, until this returns {@code false} or the end is reached.
*
* @param sequence the character sequence to examine, possibly empty
* @return {@code true} if this matcher matches every character in the sequence, including when
* the sequence is empty
*/
public boolean matchesAllOf(CharSequence sequence) {
for (int i = sequence.length() - 1; i >= 0; i--) {
if (!matches(sequence.charAt(i))) {
return false;
}
}
return true;
}
/**
* Returns {@code true} if a character sequence contains no matching characters. Equivalent to
* {@code !matchesAnyOf(sequence)}.
*
* <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
* character, until this returns {@code true} or the end is reached.
*
* @param sequence the character sequence to examine, possibly empty
* @return {@code true} if this matcher matches no characters in the sequence, including when
* the sequence is empty
*/
public boolean matchesNoneOf(CharSequence sequence) {
return indexIn(sequence) == -1;
}
/**
* Returns a {@code char} matcher that matches any character not present in the given character
* sequence.
*/
public static CharMatcher noneOf(CharSequence sequence) {
return anyOf(sequence).negate();
}
/**
* Returns a {@code char} matcher that matches any character present in the given character
* sequence.
*/
public static CharMatcher anyOf(final CharSequence sequence) {
switch (sequence.length()) {
case 0:
return none();
case 1:
return is(sequence.charAt(0));
case 2:
return isEither(sequence.charAt(0), sequence.charAt(1));
default:
// TODO(lowasser): is it potentially worth just going ahead and building a precomputed
// matcher?
return new AnyOf(sequence);
}
}
/**
* Returns the index of the first matching character in a character sequence, or {@code -1} if no
* matching character is present.
*
* <p>The default implementation iterates over the sequence in forward order calling
* {@link #matches} for each character.
*
* @param sequence the character sequence to examine from the beginning
* @return an index, or {@code -1} if no character matches
*/
public int indexIn(CharSequence sequence) {
return indexIn(sequence, 0);
}
/**
* Returns the index of the first matching character in a character sequence, starting from a
* given position, or {@code -1} if no character matches after that position.
*
* <p>The default implementation iterates over the sequence in forward order, beginning at {@code
* start}, calling {@link #matches} for each character.
*
* @param sequence the character sequence to examine
* @param start the first index to examine; must be nonnegative and no greater than {@code
* sequence.length()}
* @return the index of the first matching character, guaranteed to be no less than {@code start},
* or {@code -1} if no character matches
* @throws IndexOutOfBoundsException if start is negative or greater than {@code
* sequence.length()}
*/
public int indexIn(CharSequence sequence, int start) {
int length = sequence.length();
Preconditions.checkPositionIndex(start, length);
for (int i = start; i < length; i++) {
if (matches(sequence.charAt(i))) {
return i;
}
}
return -1;
}
/**
* Returns the number of matching characters found in a character sequence.
*/
public int countIn(CharSequence sequence) {
int count = 0;
for (int i = 0; i < sequence.length(); i++) {
if (matches(sequence.charAt(i))) {
count++;
}
}
return count;
}
/**
* Returns the Java Unicode escape sequence for the given character, in the form "\u12AB" where
* "12AB" is the four hexadecimal digits representing the 16 bits of the UTF-16 character.
*/
private static String showCharacter(char c) {
String hex = "0123456789ABCDEF";
char[] tmp = {'\\', 'u', '\0', '\0', '\0', '\0'};
for (int i = 0; i < 4; i++) {
tmp[5 - i] = hex.charAt(c & 0xF);
c = (char) (c >> 4);
}
return String.copyValueOf(tmp);
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common.http;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.function.Predicate;
import io.helidon.common.reactive.Flow;
/**
* Represents an HTTP entity as a {@link Flow.Publisher publisher} of {@link DataChunk chunks} with specific
* features.
* <h3>Default publisher contract</h3>
* Default publisher accepts only single subscriber. Other subscribers receives
* {@link Flow.Subscriber#onError(Throwable) onError()}.
* <p>
* {@link DataChunk} provided by {@link Flow.Subscriber#onNext(Object) onNext()} method <b>must</b> be consumed in this
* method call. Buffer can be reused by network infrastructure as soon as {@code onNext()} method returns.
* This behavior can be inconvenient yet it helps to provide excellent performance.
*
* <h3>Publisher Overwrite.</h3>
* It is possible to modify contract of the original publisher by registration of a new publisher using
* {@link #registerFilter(Function)} method. It can be used to wrap or replace previously registered (or default) publisher.
*
* <h3>Entity Readers</h3>
* It is possible to register function to convert publisher to {@link CompletionStage} of a single entity using
* {@link #registerReader(Class, Reader)} or {@link #registerReader(Predicate, Reader)} methods. It
* is then possible to use {@link #as(Class)} method to obtain such entity.
*/
public interface Content extends Flow.Publisher<DataChunk> {
/**
* If possible, adds the given Subscriber to this publisher. This publisher is effectively
* either the original publisher
* or the last publisher registered by the method {@link #registerFilter(Function)}.
* <p>
* Note that the original publisher allows only a single subscriber and requires the passed
* {@link DataChunk} in the {@link Flow.Subscriber#onNext(Object)} call
* to be consumed before the method completes as specified by the {@link Content Default Publisher Contract}.
*
* @param subscriber the subscriber
* @throws NullPointerException if subscriber is null
*/
@Override
void subscribe(Flow.Subscriber<? super DataChunk> subscriber);
/**
* Registers a filter that allows a control of the original publisher.
* <p>
* The provided function is evaluated upon calling either of {@link #subscribe(Flow.Subscriber)}
* or {@link #as(Class)}.
* The first evaluation of the function transforms the original publisher to a new publisher.
* Any subsequent evaluation receives the publisher transformed by the last previously
* registered filter.
* It is up to the implementor of the given function to respect the contract of both the original
* publisher and the previously registered ones.
*
* @param function a function that transforms a given publisher (that is either the original
* publisher or the publisher transformed by the last previously registered filter).
*/
void registerFilter(Function<Flow.Publisher<DataChunk>, Flow.Publisher<DataChunk>> function);
/**
* Registers a reader for a later use with an appropriate {@link #as(Class)} method call.
* <p>
* The reader must transform the published byte buffers into a completion stage of the
* requested type.
* <p>
* Upon calling {@link #as(Class)} a matching reader is searched in the same order as the
* readers were registered. If no matching reader is found, or when the function throws
* an exception, the resulting completion stage ends exceptionally.
*
* @param type the requested type the completion stage is be associated with.
* @param reader the reader as a function that transforms a publisher into completion stage.
* If an exception is thrown, the resulting completion stage of
* {@link #as(Class)} method call ends exceptionally.
* @param <T> the requested type
*/
<T> void registerReader(Class<T> type, Reader<T> reader);
/**
* Registers a reader for a later use with an appropriate {@link #as(Class)} method call.
* <p>
* The reader must transform the published byte buffers into a completion stage of the
* requested type.
* <p>
* Upon calling {@link #as(Class)} a matching reader is searched in the same order as the
* readers were registered. If no matching reader is found or when the predicate throws
* an exception, or when the function throws an exception, the resulting completion stage
* ends exceptionally.
*
* @param predicate the predicate that determines whether the registered reader can handle
* the requested type. If an exception is thrown, the resulting completion
* stage of {@link #as(Class)} method call ends exceptionally.
* @param reader the reader as a function that transforms a publisher into completion stage.
* If an exception is thrown, the resulting completion stage of
* {@link #as(Class)} method call ends exceptionally.
* @param <T> the requested type
*/
<T> void registerReader(Predicate<Class<?>> predicate, Reader<T> reader);
/**
* Consumes and converts the request content into a completion stage of the requested type.
* <p>
* The conversion requires an appropriate reader to be already registered
* (see {@link #registerReader(Predicate, Reader)}). If no such reader is found, the
* resulting completion stage ends exceptionally.
*
* @param type the requested type class
* @param <T> the requested type
* @return a completion stage of the requested type
*/
<T> CompletionStage<T> as(Class<T> type);
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.Optional;
import java.util.function.Supplier;
@@ -22,12 +22,12 @@ import java.util.function.Supplier;
/**
* A registry for context objects. Enables instance localization between several <i>services / components / ...</i> integrated in
* a particular known scope. ContextualRegistry instance is intended to be associated with a scope aware object such as
* {@link WebServer} or {@link ServerRequest}.
* WebServer, ServerRequest or ClientRequest.
*
* <p>Context contains also a notion of <i>classifiers</i>. Classifier is any object defining additional <i>key</i> for registered
* objects. To obtain such registered object, the same classifier (precisely, any equal object) has to be used.
*
* <p>Classifiers can be used as folows:<ol>
* <p>Classifiers can be used as follows:<ol>
* <li>As an additional identifier for registered objects of common types, like a {@link String}, ...<br>
* <pre>{@code
* // User detail provider service
@@ -49,6 +49,27 @@ import java.util.function.Supplier;
*/
public interface ContextualRegistry {
/**
* Creates a new empty instance.
*
* @return new instance
*/
static ContextualRegistry create() {
return new ListContextualRegistry();
}
/**
* Creates a new empty instance backed by its parent read-through {@link ContextualRegistry}.
*
* <p>Parent {@code registry} is used only for get methods and only if this registry doesn't have registered required type.
*
* @param parent a parent registry
* @return new instance
*/
static ContextualRegistry create(ContextualRegistry parent) {
return new ListContextualRegistry(parent);
}
/**
* Register a new instance.
*
@@ -96,7 +117,8 @@ public interface ContextualRegistry {
/**
* Registers a new instance using a provided supplier. The supplier is guarantied to be called at most once when it's
* requested by the {@link #get(Object, Class)} method. The returned value gets registered and the supplier is never called again.
* requested by the {@link #get(Object, Class)} method. The returned value gets registered and the supplier is never called
* again.
*
* <p>Registered instance can be obtained only using {@link #get(Object, Class)} method with a {@code classifier} equal with
* the one used during registration.
@@ -122,25 +144,4 @@ public interface ContextualRegistry {
* @throws NullPointerException If {@code classifier} is null.
*/
<T> Optional<T> get(Object classifier, Class<T> type);
/**
* Creates a new empty instance.
*
* @return new instance
*/
static ContextualRegistry create() {
return new ListContextualRegistry();
}
/**
* Creates a new empty instance backed by its parent read-through {@link ContextualRegistry}.
*
* <p>Parent {@code registry} is used only for get methods and only if this registry doesn't have registered required type.
*
* @param parent a parent registry
* @return new instance
*/
static ContextualRegistry create(ContextualRegistry parent) {
return new ListContextualRegistry(parent);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,14 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* The RequestChunk represents a part of the HTTP request body content.
* The DataChunk represents a part of the HTTP body content.
* <p>
* The ReqeustChunk and the content it carries stay immutable as long as method
* The DataChunk and the content it carries stay immutable as long as method
* {@link #release()} is not called. After that, the given instance and the associated
* data structure instances (e.g., the {@link ByteBuffer} obtained by {@link #data()})
* should not be used. The idea behind this class is to be able to
@@ -34,36 +32,76 @@ import java.nio.ByteBuffer;
* the methods of this class (such as {@link #data()}, {@link #release()} from different
* threads may result in a race condition unless an external synchronization is used.
*/
public interface RequestChunk {
@FunctionalInterface
public interface DataChunk {
/**
* Creates a simple {@link ByteBuffer} backed data chunk. The resulting
* instance doesn't have any kind of a lifecycle and as such, it doesn't need
* to be released.
*
* @param byteBuffer a byte buffer to create the request chunk from
* @return a request chunk
*/
static DataChunk create(ByteBuffer byteBuffer) {
return create(false, byteBuffer);
}
/**
* Gets the content of the underlying {@link ByteBuffer} as an array of bytes.
* If the the ByteBuffer was read, the returned array contains only the part of
* data that wasn't read yet. On the other hand, calling this method doesn't cause
* the underlying {@link ByteBuffer} to be read.
* <p>
* It is expected the returned byte array holds a reference to data that
* will become stale upon calling method {@link #release()}. (For instance,
* the memory segment is pooled by the underlying TCP server and is reused
* for a subsequent request chunk.) The idea behind this class is to be able to
* minimize data copying; ideally, in order to achieve the best performance,
* to not copy them at all. However, the implementations may choose otherwise.
* <p>
* Note that the methods of this instance are expected to be called by a single
* thread; if not, external synchronization must be used.
* Creates a simple byte array backed data chunk. The resulting
* instance doesn't have any kind of a lifecycle and as such, it doesn't need
* to be released.
*
* @return an array of bytes that is guarantied to stay immutable as long as
* method {@link #release()} is not called
* @param bytes a byte array to create the request chunk from
* @return a request chunk
*/
default byte[] bytes() {
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Utils.write(data().asReadOnlyBuffer(), stream);
return stream.toByteArray();
} catch (IOException e) {
// never happens with ByteArrayOutputStream
throw new AssertionError("ByteArrayOutputStream is not expected to throw an IO Exception.", e);
}
static DataChunk create(byte[] bytes) {
return create(false, ByteBuffer.wrap(bytes));
}
/**
* Creates a reusable data chunk.
*
* @param flush a signal that chunk should be written and flushed from any cache if possible
* @param data a data chunk. Should not be reused until {@code releaseCallback} is used
* @return a reusable data chunk with no release callback
*/
static DataChunk create(boolean flush, ByteBuffer data) {
return create(flush, data, Utils.EMPTY_RUNNABLE);
}
/**
* Creates a reusable data chunk.
*
* @param flush a signal that chunk should be written and flushed from any cache if possible
* @param data a data chunk. Should not be reused until {@code releaseCallback} is used
* @param releaseCallback a callback which is called when this chunk is completely processed and instance is free for reuse
* @return a reusable data chunk with a release callback
*/
static DataChunk create(boolean flush, ByteBuffer data, Runnable releaseCallback) {
return new DataChunk() {
private boolean isReleased = false;
@Override
public ByteBuffer data() {
return data;
}
@Override
public boolean flush() {
return flush;
}
@Override
public void release() {
releaseCallback.run();
isReleased = true;
}
@Override
public boolean isReleased() {
return isReleased;
}
};
}
/**
@@ -87,6 +125,38 @@ public interface RequestChunk {
*/
ByteBuffer data();
/**
* The tracing ID of this chunk.
*
* @return the tracing ID of this chunk
*/
default long id() {
return System.identityHashCode(this);
}
/**
* Gets the content of the underlying {@link ByteBuffer} as an array of bytes.
* If the the ByteBuffer was read, the returned array contains only the part of
* data that wasn't read yet. On the other hand, calling this method doesn't cause
* the underlying {@link ByteBuffer} to be read.
* <p>
* It is expected the returned byte array holds a reference to data that
* will become stale upon calling method {@link #release()}. (For instance,
* the memory segment is pooled by the underlying TCP server and is reused
* for a subsequent request chunk.) The idea behind this class is to be able to
* minimize data copying; ideally, in order to achieve the best performance,
* to not copy them at all. However, the implementations may choose otherwise.
* <p>
* Note that the methods of this instance are expected to be called by a single
* thread; if not, external synchronization must be used.
*
* @return an array of bytes that is guarantied to stay immutable as long as
* method {@link #release()} is not called
*/
default byte[] bytes() {
return Utils.toByteArray(data().asReadOnlyBuffer());
}
/**
* Whether this chunk is released and the associated data structures returned
* by methods (such as {@link #data()} or {@link #bytes()}) should not be used.
@@ -96,9 +166,11 @@ public interface RequestChunk {
* Note that the methods of this instance are expected to be called by a single
* thread; if not, external synchronization must be used.
*
* @return whether this chunk has been released
* @return whether this chunk has been released, defaults to false
*/
boolean isReleased();
default boolean isReleased() {
return false;
}
/**
* Releases this chunk. The underlying data as well as the data structure instances returned by
@@ -109,38 +181,17 @@ public interface RequestChunk {
* Note that the methods of this instance are expected to be called by a single
* thread; if not, external synchronization must be used.
*/
void release();
/**
* The tracing ID of this chunk.
*
* @return the tracing ID of this chunk
*/
default long id() {
return System.identityHashCode(this);
default void release() {
}
/**
* Creates a simple {@link ByteBuffer} backed request chunk. The resulting
* instance doesn't have any kind of a lifecycle and as such, it doesn't need
* to be released.
* Returns {@code true} if all caches are requested to flush when this chunk is written.
* This method is only meaningful when handing data over to
* Helidon APIs (e.g. for server response and client requests).
*
* @param byteBuffer a byte buffer to create the request chunk from
* @return a request chunk
* @return {@code true} if it is requested to flush all caches after this chunk is written, defaults to {@code false}.
*/
static RequestChunk from(ByteBuffer byteBuffer) {
return new ByteBufferRequestChunk(byteBuffer);
}
/**
* Creates a simple byte array backed request chunk. The resulting
* instance doesn't have any kind of a lifecycle and as such, it doesn't need
* to be released.
*
* @param bytes a byte array to create the request chunk from
* @return a request chunk
*/
static RequestChunk from(byte[] bytes) {
return new ByteBufferRequestChunk(ByteBuffer.wrap(bytes));
default boolean flush() {
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.List;
import java.util.Optional;
@@ -22,9 +22,6 @@ import java.util.stream.Collectors;
/**
* Extends {@link Parameters} interface by adding methods convenient for HTTP headers.
*
* @see RequestHeaders
* @see ResponseHeaders
*/
public interface Headers extends Parameters {

View File

@@ -0,0 +1,125 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common.http;
import java.net.URI;
import java.util.List;
/**
* Common attributes of an HTTP Request, that are used both in server requests and in client requests.
*/
public interface HttpRequest {
/**
* Returns an HTTP request method. See also {@link Http.Method HTTP standard methods} utility class.
*
* @return an HTTP method
* @see Http.Method
*/
Http.RequestMethod method();
/**
* Returns an HTTP version from the request line.
* <p>
* See {@link Http.Version HTTP Version} enumeration for supported versions.
* <p>
* If communication starts as a {@code HTTP/1.1} with {@code h2c} upgrade, then it will be automatically
* upgraded and this method returns {@code HTTP/2.0}.
*
* @return an HTTP version
*/
Http.Version version();
/**
* Returns a Request-URI (or alternatively path) as defined in request line.
*
* @return a request URI
*/
URI uri();
/**
* Returns an encoded query string without leading '?' character.
*
* @return an encoded query string
*/
String query();
/**
* Returns query parameters.
*
* @return an parameters representing query parameters
*/
Parameters queryParams();
/**
* Returns a path which was accepted by matcher in actual routing. It is path without a context root
* of the routing.
* <p>
* Use {@link Path#absolute()} method to obtain absolute request URI path representation.
* <p>
* Returned {@link Path} also provide access to path template parameters. An absolute path then provides access to
* all (including) context parameters if any. In case of conflict between parameter names, most recent value is returned.
*
* @return a path
*/
Path path();
/**
* Returns a decoded request URI fragment without leading hash '#' character.
*
* @return a decoded URI fragment
*/
String fragment();
/**
* Represents requested normalised URI path.
*/
interface Path {
/**
* Returns value of single parameter resolved from path pattern.
*
* @param name a parameter name
* @return a parameter value or {@code null} if not exist
*/
String param(String name);
/**
* Returns path as a list of its segments.
*
* @return a list of path segments
*/
List<String> segments();
/**
* Returns a path string representation with leading slash.
*
* @return a path
*/
String toString();
/**
* If the instance represents a path relative to some context root then returns absolute requested path otherwise
* returns this instance.
* <p>
* The absolute path also contains access to path parameters defined in context matchers. If there is
* name conflict then value represents latest matcher result.
*
* @return an absolute requested URI path
*/
Path absolute();
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.ArrayList;
import java.util.List;
@@ -109,6 +109,12 @@ class ListContextualRegistry implements ContextualRegistry {
}
}
private interface RegisteredItem<T> {
T get();
Class<T> getType();
}
private static class ClassifiedRegistry {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final List<RegisteredItem> content = new ArrayList<>();
@@ -162,11 +168,6 @@ class ListContextualRegistry implements ContextualRegistry {
}
}
private interface RegisteredItem<T> {
T get();
Class<T> getType();
}
private static class RegisteredSupplier<T> implements RegisteredItem<T> {
private final Class<T> type;
private final Supplier<T> supplier;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.Collections;
import java.util.HashMap;
@@ -31,118 +31,98 @@ import java.util.function.Supplier;
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP/1.1 section 3.7</a>
*/
public class MediaType implements AcceptPredicate<MediaType> {
// must be first, as this is used to create instances of media types
private static final Map<MediaType, MediaType> KNOWN_TYPES = new HashMap<>();
/**
* The media type {@value CHARSET_PARAMETER} parameter name.
*/
public static final String CHARSET_PARAMETER = "charset";
private static final Map<MediaType, MediaType> KNOWN_TYPES = new HashMap<>();
private static MediaType createMediaType() {
MediaType mediaType = new MediaType();
KNOWN_TYPES.put(mediaType, mediaType);
return mediaType;
}
private static MediaType createMediaType(String type, String subtype) {
MediaType mediaType = new MediaType(type, subtype);
KNOWN_TYPES.put(mediaType, mediaType);
return mediaType;
}
// Common media type constants
/**
* A {@link MediaType} constant representing wildcard media type.
*/
public static final MediaType WILDCARD = createMediaType();
// Common media type constants
/**
* A {@link MediaType} constant representing {@code application/xml} media type.
*/
public static final MediaType APPLICATION_XML = createMediaType("application", "xml");
/**
* A {@link MediaType} constant representing {@code application/atom+xml} media type.
*/
public static final MediaType APPLICATION_ATOM_XML = createMediaType("application", "atom+xml");
/**
* A {@link MediaType} constant representing {@code application/xhtml+xml} media type.
*/
public static final MediaType APPLICATION_XHTML_XML = createMediaType("application", "xhtml+xml");
/**
* A {@link MediaType} constant representing {@code application/svg+xml} media type.
*/
public static final MediaType APPLICATION_SVG_XML = createMediaType("application", "svg+xml");
/**
* A {@link MediaType} constant representing {@code application/json} media type.
*/
public static final MediaType APPLICATION_JSON = createMediaType("application", "json");
/**
* A {@link MediaType} constant representing {@code application/x-www-form-urlencoded} media type.
*/
public static final MediaType APPLICATION_FORM_URLENCODED = createMediaType("application", "x-www-form-urlencoded");
/**
* A {@link MediaType} constant representing {@code multipart/form-data} media type.
*/
public static final MediaType MULTIPART_FORM_DATA = createMediaType("multipart", "form-data");
/**
* A {@link MediaType} constant representing {@code application/octet-stream} media type.
*/
public static final MediaType APPLICATION_OCTET_STREAM = createMediaType("application", "octet-stream");
/**
* A {@link MediaType} constant representing {@code text/plain} media type.
*/
public static final MediaType TEXT_PLAIN = createMediaType("text", "plain");
/**
* A {@link MediaType} constant representing {@code text/xml} media type.
*/
public static final MediaType TEXT_XML = createMediaType("text", "xml");
/**
* A {@link MediaType} constant representing {@code text/html} media type.
*/
public static final MediaType TEXT_HTML = createMediaType("text", "html");
// Common predicates
private static final MediaType APPLICATION_JAVASCRIPT = createMediaType("application", "javascript");
// Common predicates
/**
* Predicate to test if {@link MediaType} is {@code application/xml} or {@code text/xml} or has {@code xml} suffix.
*/
public static final Predicate<MediaType> XML_PREDICATE = APPLICATION_XML.or(TEXT_XML).or(mt -> mt.hasSuffix("xml"));
/**
* Predicate to test if {@link MediaType} is {@code application/json} or has {@code json} suffix.
*/
public static final Predicate<MediaType> JSON_PREDICATE = APPLICATION_JSON
.or(APPLICATION_JAVASCRIPT)
.or(mt -> mt.hasSuffix("json"));
/**
* Predicate to test if {@link MediaType} is {@code application/xml} or {@code text/xml} or has {@code xml} suffix.
* Matcher for type, subtype and attributes.
*/
public static final Predicate<MediaType> XML_PREDICATE = APPLICATION_XML.or(TEXT_XML).or(mt -> mt.hasSuffix("xml"));
private static final CharMatcher TOKEN_MATCHER =
CharMatcher.ascii()
.and(CharMatcher.javaIsoControl().negate())
.and(CharMatcher.isNot(' '))
.and(CharMatcher.noneOf("()<>@,;:\\\"/[]?="));
private static final CharMatcher QUOTED_TEXT_MATCHER = CharMatcher.ascii().and(CharMatcher.noneOf("\"\\\r"));
/*
* This matches the same characters as linear-white-space from RFC 822, but we make no effort to
* enforce any particular rules with regards to line folding as stated in the class docs.
*/
private static final CharMatcher LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n");
private static final String CHARSET_ATTRIBUTE = "charset";
private String type;
private String subtype;
private Map<String, String> parameters;
private static TreeMap<String, String> createParametersMap(Map<String, String> initialValues) {
final TreeMap<String, String> map = new TreeMap<>(String::compareToIgnoreCase);
if (initialValues != null) {
for (Map.Entry<String, String> e : initialValues.entrySet()) {
map.put(e.getKey().toLowerCase(), e.getValue());
}
}
return map;
}
/**
* Creates a new instance of {@code MediaType} with the supplied type, subtype and
* parameters.
@@ -187,13 +167,13 @@ public class MediaType implements AcceptPredicate<MediaType> {
* Consider using the constant {@link #WILDCARD_VALUE} instead.
*/
public MediaType() {
this(WILDCARD_VALUE, WILDCARD_VALUE, null, null);
this(AcceptPredicate.WILDCARD_VALUE, AcceptPredicate.WILDCARD_VALUE, null, null);
}
private MediaType(String type, String subtype, String charset, Map<String, String> parameterMap) {
this.type = type == null ? WILDCARD_VALUE : type;
this.subtype = subtype == null ? WILDCARD_VALUE : subtype;
this.type = type == null ? AcceptPredicate.WILDCARD_VALUE : type;
this.subtype = subtype == null ? AcceptPredicate.WILDCARD_VALUE : subtype;
if (parameterMap == null) {
parameterMap = new TreeMap<>(String::compareToIgnoreCase);
@@ -205,120 +185,35 @@ public class MediaType implements AcceptPredicate<MediaType> {
this.parameters = Collections.unmodifiableMap(parameterMap);
}
/**
* Getter for primary type.
*
* @return value of primary type.
*/
public String getType() {
return this.type;
private static MediaType createMediaType() {
MediaType mediaType = new MediaType();
KNOWN_TYPES.put(mediaType, mediaType);
return mediaType;
}
/**
* Checks if the primary type is a wildcard.
*
* @return true if the primary type is a wildcard.
*/
public boolean isWildcardType() {
return this.getType().equals(WILDCARD_VALUE);
private static MediaType createMediaType(String type, String subtype) {
MediaType mediaType = new MediaType(type, subtype);
KNOWN_TYPES.put(mediaType, mediaType);
return mediaType;
}
/**
* Getter for subtype.
*
* @return value of subtype.
*/
public String getSubtype() {
return this.subtype;
private static TreeMap<String, String> createParametersMap(Map<String, String> initialValues) {
final TreeMap<String, String> map = new TreeMap<>(String::compareToIgnoreCase);
if (initialValues != null) {
for (Map.Entry<String, String> e : initialValues.entrySet()) {
map.put(e.getKey().toLowerCase(), e.getValue());
}
}
return map;
}
/**
* Checks if the subtype is a wildcard.
*
* @return true if the subtype is a wildcard.
*/
public boolean isWildcardSubtype() {
return this.getSubtype().equals(WILDCARD_VALUE);
}
/**
* Getter for a read-only parameter map. Keys are case-insensitive.
*
* @return an immutable map of parameters.
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Create a new {@code MediaType} instance with the same type, subtype and parameters
* copied from the original instance and the supplied {@value #CHARSET_PARAMETER} parameter.
*
* @param charset the {@value #CHARSET_PARAMETER} parameter value. If {@code null} or empty
* the {@value #CHARSET_PARAMETER} parameter will not be set or updated.
* @return copy of the current {@code MediaType} instance with the {@value #CHARSET_PARAMETER}
* parameter set to the supplied value.
* @since 2.0
*/
public MediaType withCharset(String charset) {
return new MediaType(this.type, this.subtype, charset, createParametersMap(this.parameters));
}
/**
* Gets {@link Optional} value of charset parameter.
*
* @return Charset parameter.
*/
public Optional<String> getCharset() {
return Optional.ofNullable(parameters.get(CHARSET_PARAMETER));
}
@Override
public double qualityFactor() {
String q = parameters.get(QUALITY_FACTOR_PARAMETER);
return q == null ? 1D : Double.valueOf(q);
}
/**
* Check if this media type is compatible with another media type. E.g.
* image/* is compatible with image/jpeg, image/png, etc. Media type
* parameters are ignored. The function is commutative.
*
* @param other the media type to compare with.
* @return true if the types are compatible, false otherwise.
*/
// fixme: Bidirectional wildcard compatibility
public boolean test(MediaType other) {
return other != null && // return false if other is null, else
(type.equals(WILDCARD_VALUE)
|| other.type.equals(WILDCARD_VALUE)
|| (type.equalsIgnoreCase(other.type)
&& (subtype.equals(WILDCARD_VALUE) || other.subtype.equals(WILDCARD_VALUE)))
|| (type.equalsIgnoreCase(other.type) && this.subtype.equalsIgnoreCase(other.subtype)));
}
/** Matcher for type, subtype and attributes. */
private static final CharMatcher TOKEN_MATCHER =
CharMatcher.ascii()
.and(CharMatcher.javaIsoControl().negate())
.and(CharMatcher.isNot(' '))
.and(CharMatcher.noneOf("()<>@,;:\\\"/[]?="));
private static final CharMatcher QUOTED_TEXT_MATCHER = CharMatcher.ascii().and(CharMatcher.noneOf("\"\\\r"));
/*
* This matches the same characters as linear-white-space from RFC 822, but we make no effort to
* enforce any particular rules with regards to line folding as stated in the class docs.
*/
private static final CharMatcher LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n");
private static final String CHARSET_ATTRIBUTE = "charset";
/**
* Parses a media type from its string representation.
*
* @param input the input string representing a media type
* @throws IllegalArgumentException if the input is not parsable
* @throws NullPointerException if the input is {@code null}
* @return parsed {@link MediaType} instance
* @throws IllegalArgumentException if the input is not parsable
* @throws NullPointerException if the input is {@code null}
*/
public static MediaType parse(String input) {
Objects.requireNonNull(input, "Parameter 'input' is null!");
@@ -359,55 +254,6 @@ public class MediaType implements AcceptPredicate<MediaType> {
}
}
@SuppressWarnings("checkstyle:VisibilityModifier")
private static final class Tokenizer {
final String input;
int position = 0;
Tokenizer(String input) {
this.input = input;
}
String consumeTokenIfPresent(CharMatcher matcher) {
checkState(hasMore(), "No more elements!");
int startPosition = position;
position = matcher.negate().indexIn(input, startPosition);
return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition);
}
String consumeToken(CharMatcher matcher) {
int startPosition = position;
String token = consumeTokenIfPresent(matcher);
checkState(position != startPosition, () ->
String.format("Position '%d' should not be '%d'!", position, startPosition));
return token;
}
char consumeCharacter(CharMatcher matcher) {
checkState(hasMore(), "No more elements!");
char c = previewChar();
checkState(matcher.matches(c), "Unexpected character matched: " + c);
position++;
return c;
}
char consumeCharacter(char c) {
checkState(hasMore(), "No more elements!");
checkState(previewChar() == c, () -> "Unexpected character: " + c);
position++;
return c;
}
char previewChar() {
checkState(hasMore(), "No more elements!");
return input.charAt(position);
}
boolean hasMore() {
return (position >= 0) && (position < input.length());
}
}
/**
* Ensures the truth of an expression involving the state of the calling instance, but not
* involving any parameters to the calling method.
@@ -465,6 +311,102 @@ public class MediaType implements AcceptPredicate<MediaType> {
return CHARSET_ATTRIBUTE.equals(attribute) ? Ascii.toLowerCase(value) : value;
}
/**
* Getter for primary type.
*
* @return value of primary type.
*/
public String getType() {
return this.type;
}
/**
* Checks if the primary type is a wildcard.
*
* @return true if the primary type is a wildcard.
*/
public boolean isWildcardType() {
return this.getType().equals(AcceptPredicate.WILDCARD_VALUE);
}
/**
* Getter for subtype.
*
* @return value of subtype.
*/
public String getSubtype() {
return this.subtype;
}
/**
* Checks if the subtype is a wildcard.
*
* @return true if the subtype is a wildcard.
*/
public boolean isWildcardSubtype() {
return this.getSubtype().equals(AcceptPredicate.WILDCARD_VALUE);
}
/**
* Getter for a read-only parameter map. Keys are case-insensitive.
*
* @return an immutable map of parameters.
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Create a new {@code MediaType} instance with the same type, subtype and parameters
* copied from the original instance and the supplied {@value #CHARSET_PARAMETER} parameter.
*
* @param charset the {@value #CHARSET_PARAMETER} parameter value. If {@code null} or empty
* the {@value #CHARSET_PARAMETER} parameter will not be set or updated.
* @return copy of the current {@code MediaType} instance with the {@value #CHARSET_PARAMETER}
* parameter set to the supplied value.
* @since 2.0
*/
public MediaType withCharset(String charset) {
return new MediaType(this.type, this.subtype, charset, createParametersMap(this.parameters));
}
/**
* Gets {@link Optional} value of charset parameter.
*
* @return Charset parameter.
*/
public Optional<String> getCharset() {
return Optional.ofNullable(parameters.get(CHARSET_PARAMETER));
}
@Override
public double qualityFactor() {
String q = parameters.get(AcceptPredicate.QUALITY_FACTOR_PARAMETER);
return q == null ? 1D : Double.valueOf(q);
}
/**
* Check if this media type is compatible with another media type. E.g.
* image/* is compatible with image/jpeg, image/png, etc. Media type
* parameters are ignored. The function is commutative.
*
* @param other the media type to compare with.
* @return true if the types are compatible, false otherwise.
*/
// fixme: Bidirectional wildcard compatibility
public boolean test(MediaType other) {
return other != null && // return false if other is null, else
(
type.equals(AcceptPredicate.WILDCARD_VALUE)
|| other.type.equals(AcceptPredicate.WILDCARD_VALUE)
|| (
type.equalsIgnoreCase(other.type)
&& (
subtype.equals(AcceptPredicate.WILDCARD_VALUE) || other.subtype
.equals(AcceptPredicate.WILDCARD_VALUE)))
|| (type.equalsIgnoreCase(other.type) && this.subtype.equalsIgnoreCase(other.subtype)));
}
/**
* Compares {@code obj} to this media type to see if they are the same by comparing
* type, subtype and parameters. Note that the case-sensitivity of parameter
@@ -491,7 +433,8 @@ public class MediaType implements AcceptPredicate<MediaType> {
}
MediaType other = (MediaType) obj;
return (this.type.equalsIgnoreCase(other.type)
return (
this.type.equalsIgnoreCase(other.type)
&& this.subtype.equalsIgnoreCase(other.subtype)
&& this.parameters.equals(other.parameters));
}
@@ -549,4 +492,53 @@ public class MediaType implements AcceptPredicate<MediaType> {
}
}
@SuppressWarnings("checkstyle:VisibilityModifier")
private static final class Tokenizer {
final String input;
int position = 0;
Tokenizer(String input) {
this.input = input;
}
String consumeTokenIfPresent(CharMatcher matcher) {
checkState(hasMore(), "No more elements!");
int startPosition = position;
position = matcher.negate().indexIn(input, startPosition);
return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition);
}
String consumeToken(CharMatcher matcher) {
int startPosition = position;
String token = consumeTokenIfPresent(matcher);
checkState(position != startPosition, () ->
String.format("Position '%d' should not be '%d'!", position, startPosition));
return token;
}
char consumeCharacter(CharMatcher matcher) {
checkState(hasMore(), "No more elements!");
char c = previewChar();
checkState(matcher.matches(c), "Unexpected character matched: " + c);
position++;
return c;
}
char consumeCharacter(char c) {
checkState(hasMore(), "No more elements!");
checkState(previewChar() == c, () -> "Unexpected character: " + c);
position++;
return c;
}
char previewChar() {
checkState(hasMore(), "No more elements!");
return input.charAt(position);
}
boolean hasMore() {
return (position >= 0) && (position < input.length());
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.List;
import java.util.Map;
@@ -25,7 +25,7 @@ import java.util.function.Function;
/**
* Parameters represents {@code key : value} pairs where {@code key} is a {@code String} with potentially multiple values.
* <p>
* This structure represents query parameters, headers and path parameters in {@link ServerRequest} and {@link ServerResponse}.
* This structure represents query parameters, headers and path parameters in e.g. {@link HttpRequest}.
* <p>
* Interface focus on most convenient use cases in HTTP Request and Response processing, like
* <pre>
@@ -39,8 +39,8 @@ import java.util.function.Function;
* <p>
* Mutable operations are defined in two forms:
* <ul>
* <li>{@code put...} create or replace association.</li>
* <li>{@code add...} create association or add values to existing association.</li>
* <li>{@code put...} create or replace association.</li>
* <li>{@code add...} create association or add values to existing association.</li>
* </ul>
* <p>
* It is possible to use {@link #toMap()} method to get immutable map view of data.
@@ -49,6 +49,18 @@ import java.util.function.Function;
*/
public interface Parameters {
/**
* Returns an unmodifiable view.
*
* @param parameters a parameters for unmodifiable view.
* @return An unmodifiable view.
* @throws NullPointerException if parameter {@code parameters} is null.
*/
static Parameters toUnmodifiableParameters(Parameters parameters) {
Objects.requireNonNull(parameters, "Parameter 'parameters' is null!");
return new UnmodifiableParameters(parameters);
}
/**
* Returns an {@link Optional} containing the first value of the given
* parameter (and possibly multi-valued) parameter. If the parameter is
@@ -75,11 +87,10 @@ public interface Parameters {
* Associates specified values with the specified key (optional operation).
* If parameters previously contained a mapping for the key, the old values fully replaced.
*
* @param key key with which the specified value is to be associated
* @param key key with which the specified value is to be associated
* @param values value to be associated with the specified key
* @return the previous values associated with key, or empty {@code List} if there was no mapping for key.
*
* @throws NullPointerException if the specified key is null.
* @throws NullPointerException if the specified key is null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
List<String> put(String key, String... values);
@@ -88,11 +99,10 @@ public interface Parameters {
* Associates specified values with the specified key (optional operation).
* If parameters previously contained a mapping for the key, the old values fully replaced.
*
* @param key key with which the specified value is to be associated
* @param key key with which the specified value is to be associated
* @param values value to be associated with the specified key. If {@code null} then association will be removed.
* @return the previous values associated with key, or empty {@code List} if there was no mapping for key.
*
* @throws NullPointerException if the specified key is null.
* @throws NullPointerException if the specified key is null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
List<String> put(String key, Iterable<String> values);
@@ -101,11 +111,10 @@ public interface Parameters {
* If the specified key is not already associated with a value associates it with the given value and returns empty
* {@code List}, else returns the current value (optional operation).
*
* @param key key with which the specified value is to be associated
* @param key key with which the specified value is to be associated
* @param values value to be associated with the specified key
* @return the previous values associated with key, or empty {@code List} if there was no mapping for key.
*
* @throws NullPointerException if the specified key is null.
* @throws NullPointerException if the specified key is null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
List<String> putIfAbsent(String key, String... values);
@@ -114,11 +123,10 @@ public interface Parameters {
* If the specified key is not already associated with a value associates it with the given value and returns empty
* {@code List}, else returns the current value (optional operation).
*
* @param key key with which the specified value is to be associated
* @param key key with which the specified value is to be associated
* @param values value to be associated with the specified key
* @return the previous values associated with key, or empty {@code List} if there was no mapping for key.
*
* @throws NullPointerException if the specified key is null.
* @throws NullPointerException if the specified key is null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
List<String> putIfAbsent(String key, Iterable<String> values);
@@ -127,18 +135,17 @@ public interface Parameters {
* If the specified key is not already associated with a value computes new association using the given function and returns
* empty {@code List}, else returns the current value (optional operation).
*
* @param key key with which the specified value is to be associated
* @param key key with which the specified value is to be associated
* @param values value to be associated with the specified key
* @return the current (potentially computed) values associated with key,
* or empty {@code List} if function returns {@code null}
*
* @throws NullPointerException if the specified key is null
* or empty {@code List} if function returns {@code null}
* @throws NullPointerException if the specified key is null
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters)
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the mappingFunction does so,
* in which case the mapping is left unestablished
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the mappingFunction does so,
* in which case the mapping is left unestablished
*/
List<String> computeIfAbsent(String key, Function<String, Iterable<String>> values);
@@ -146,18 +153,17 @@ public interface Parameters {
* If the specified key is not already associated with a value computes new association using the given function and returns
* empty {@code List}, else returns the current value (optional operation).
*
* @param key a key with which the specified value is to be associated
* @param key a key with which the specified value is to be associated
* @param value a single value to be associated with the specified key
* @return the current (potentially computed) values associated with key,
* or empty {@code List} if function returns {@code null}
*
* @throws NullPointerException if the specified key is null.
* or empty {@code List} if function returns {@code null}
* @throws NullPointerException if the specified key is null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the mappingFunction does so,
* in which case the mapping is left unestablished
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the mappingFunction does so,
* in which case the mapping is left unestablished
*/
List<String> computeSingleIfAbsent(String key, Function<String, String> value);
@@ -166,8 +172,7 @@ public interface Parameters {
* (optional operation).
*
* @param parameters to copy.
*
* @throws NullPointerException if the specified {@code parameters} are null.
* @throws NullPointerException if the specified {@code parameters} are null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
void putAll(Parameters parameters);
@@ -176,10 +181,9 @@ public interface Parameters {
* Adds specified values tu association with the specified key (optional operation).
* If parameters doesn't contains mapping, new mapping is created.
*
* @param key key with which the specified value is to be associated
* @param key key with which the specified value is to be associated
* @param values value to be add to association with the specified key
*
* @throws NullPointerException if the specified key is null.
* @throws NullPointerException if the specified key is null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
void add(String key, String... values);
@@ -188,10 +192,9 @@ public interface Parameters {
* Adds specified values tu association with the specified key (optional operation).
* If parameters doesn't contains mapping, new mapping is created.
*
* @param key key with which the specified value is to be associated
* @param key key with which the specified value is to be associated
* @param values value to be add to association with the specified key. If {@code null} then noting will be add.
*
* @throws NullPointerException if the specified key is null.
* @throws NullPointerException if the specified key is null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
void add(String key, Iterable<String> values);
@@ -201,8 +204,7 @@ public interface Parameters {
* (optional operation).
*
* @param parameters to copy.
*
* @throws NullPointerException if the specified {@code parameters} are null.
* @throws NullPointerException if the specified {@code parameters} are null.
* @throws UnsupportedOperationException if put operation is not supported (unmodifiable Parameters).
*/
void addAll(Parameters parameters);
@@ -223,16 +225,4 @@ public interface Parameters {
* @return the {@code Map}
*/
Map<String, List<String>> toMap();
/**
* Returns an unmodifiable view.
*
* @param parameters a parameters for unmodifiable view.
* @return An unmodifiable view.
* @throws NullPointerException if parameter {@code parameters} is null.
*/
static Parameters toUnmodifiableParameters(Parameters parameters) {
Objects.requireNonNull("Parameter 'parameters' is null!");
return new UnmodifiableParameters(parameters);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
/**
* Copied from Guava.
@@ -95,10 +95,10 @@ final class Preconditions {
* size {@code size}. A position index may range from zero to {@code size}, inclusive.
*
* @param index a user-supplied index identifying a position in an array, list or string
* @param size the size of that array, list or string
* @param size the size of that array, list or string
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
* @throws IllegalArgumentException if {@code size} is negative
*/
public static int checkPositionIndex(int index, int size) {
return checkPositionIndex(index, size, "index");
@@ -109,11 +109,11 @@ final class Preconditions {
* size {@code size}. A position index may range from zero to {@code size}, inclusive.
*
* @param index a user-supplied index identifying a position in an array, list or string
* @param size the size of that array, list or string
* @param desc the text to use to describe this index in an error message
* @param size the size of that array, list or string
* @param desc the text to use to describe this index in an error message
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
* @throws IllegalArgumentException if {@code size} is negative
*/
public static int checkPositionIndex(int index, int size, String desc) {
// Carefully optimized for execution by hotspot (explanatory comment above)
@@ -129,11 +129,11 @@ final class Preconditions {
* {@code size}, inclusive.
*
* @param start a user-supplied index identifying a starting position in an array, list or string
* @param end a user-supplied index identifying a ending position in an array, list or string
* @param size the size of that array, list or string
* @param end a user-supplied index identifying a ending position in an array, list or string
* @param size the size of that array, list or string
* @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size},
* or if {@code end} is less than {@code start}
* @throws IllegalArgumentException if {@code size} is negative
* or if {@code end} is less than {@code start}
* @throws IllegalArgumentException if {@code size} is negative
*/
public static void checkPositionIndexes(int start, int end, int size) {
// Carefully optimized for execution by hotspot (explanatory comment above)
@@ -153,7 +153,6 @@ final class Preconditions {
return format("end index (%s) must not be less than start index (%s)", end, start);
}
private static String badPositionIndex(int index, int size, String desc) {
if (index < 0) {
return format("%s (%s) must not be negative", desc, index);
@@ -171,8 +170,8 @@ final class Preconditions {
* square braces.
*
* @param template a non-null string containing 0 or more {@code %s} placeholders.
* @param args the arguments to be substituted into the message template. Arguments are converted
* to strings using {@link String#valueOf(Object)}. Arguments can be null.
* @param args the arguments to be substituted into the message template. Arguments are converted
* to strings using {@link String#valueOf(Object)}. Arguments can be null.
*/
// Note that this is somewhat-improperly used from Verify.java as well.
static String format(String template, Object... args) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.ArrayList;
import java.util.Collections;
@@ -57,6 +57,31 @@ public class ReadOnlyParameters implements Parameters {
this(parameters == null ? null : parameters.toMap());
}
/**
* Returns empty and immutable singleton.
*
* @return the parameters singleton instance which is empty and immutable.
*/
public static ReadOnlyParameters empty() {
return EMPTY;
}
/**
* Returns a deep copy of provided multi-map which is completely unmodifiable.
*
* @param data data to copy, if {@code null} then returns empty map.
* @return unmodifiable map, never {@code null}.
*/
static Map<String, List<String>> copyMultimapAsImutable(Map<String, List<String>> data) {
if (data == null || data.isEmpty()) {
return Collections.emptyMap();
} else {
// Deep copy
Map<String, List<String>> h = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
data.forEach((k, v) -> h.put(k, Collections.unmodifiableList(new ArrayList<>(v))));
return Collections.unmodifiableMap(h);
}
}
@Override
public Optional<String> first(String name) {
@@ -129,30 +154,4 @@ public class ReadOnlyParameters implements Parameters {
data.forEach((k, v) -> h.put(k, new ArrayList<>(v)));
return h;
}
/**
* Returns empty and immutable singleton.
*
* @return the parameters singleton instance which is empty and immutable.
*/
public static ReadOnlyParameters empty() {
return EMPTY;
}
/**
* Returns a deep copy of provided multi-map which is completely unmodifiable.
*
* @param data data to copy, if {@code null} then returns empty map.
* @return unmodifiable map, never {@code null}.
*/
static Map<String, List<String>> copyMultimapAsImutable(Map<String, List<String>> data) {
if (data == null || data.isEmpty()) {
return Collections.emptyMap();
} else {
// Deep copy
Map<String, List<String>> h = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
data.forEach((k, v) -> h.put(k, Collections.unmodifiableList(new ArrayList<>(v))));
return Collections.unmodifiableMap(h);
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common.http;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import io.helidon.common.reactive.Flow;
/**
* The Reader transforms a {@link DataChunk} publisher into a completion stage of the associated type.
*
* @param <R> the requested type
*/
@FunctionalInterface
public interface Reader<R> extends BiFunction<Flow.Publisher<DataChunk>, Class<? super R>, CompletionStage<? extends R>> {
/**
* Transforms a publisher into a completion stage.
* If an exception is thrown, the resulting completion stage of
* {@link Content#as(Class)} method call ends exceptionally.
*
* @param publisher the publisher to transform
* @param clazz the requested type to be returned as a completion stage. The purpose of
* this parameter is to know what the user of this Reader actually requested.
* @return the result as a completion stage
*/
@Override
CompletionStage<? extends R> apply(Flow.Publisher<DataChunk> publisher, Class<? super R> clazz);
/**
* Transforms a publisher into a completion stage.
* If an exception is thrown, the resulting completion stage of
* {@link Content#as(Class)} method call ends exceptionally.
* <p>
* The default implementation calls {@link #apply(Flow.Publisher, Class)} with {@link Object} as
* the class parameter.
*
* @param publisher the publisher to transform
* @return the result as a completion stage
*/
default CompletionStage<? extends R> apply(Flow.Publisher<DataChunk> publisher) {
return apply(publisher, Object.class);
}
/**
* Transforms a publisher into a completion stage.
* If an exception is thrown, the resulting completion stage of
* {@link Content#as(Class)} method call ends exceptionally.
* <p>
* The default implementation calls {@link #apply(Flow.Publisher, Class)} with {@link Object} as
* the class parameter.
*
* @param publisher the publisher to transform
* @param type the desired type to cast the guarantied {@code R} type to
* @param <T> the desired type to cast the guarantied {@code R} type to
* @return the result as a completion stage which might end exceptionally with
* {@link ClassCastException} if the {@code R} type wasn't possible to cast
* to {@code T}
*/
default <T extends R> CompletionStage<? extends T> applyAndCast(Flow.Publisher<DataChunk> publisher, Class<T> type) {
// if this was implemented as (CompletionStage<? extends T>) apply(publisher, (Class<R>) clazz);
// the class cast exception might occur outside of the completion stage which might be confusing
return apply(publisher, (Class<R>) type).thenApply(type::cast);
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common.http;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Objects;
/**
* Represents {@code 'Set-Cookie'} header value specified by <a href="https://tools.ietf.org/html/rfc6265">RFC6265</a>.
*
* <p>It is mutable and fluent builder.
*/
public class SetCookie {
private static final String PARAM_SEPARATOR = "; ";
private final String name;
private final String value;
private ZonedDateTime expires;
private Duration maxAge;
private String domain;
private String path;
private boolean secure;
private boolean httpOnly;
/**
* Creates new instance.
*
* @param name a cookie name.
* @param value a cookie value.
*/
public SetCookie(String name, String value) {
Objects.requireNonNull(name, "Parameter 'name' is null!");
//todo validate accepted characters
this.name = name;
this.value = value;
}
/**
* Sets {@code Expires} parameter.
*
* @param expires an {@code Expires} parameter.
* @return Updated instance.
*/
public SetCookie expires(ZonedDateTime expires) {
this.expires = expires;
return this;
}
/**
* Sets {@code Expires} parameter.
*
* @param expires an {@code Expires} parameter.
* @return Updated instance.
*/
public SetCookie expires(Instant expires) {
if (expires == null) {
this.expires = null;
} else {
this.expires = ZonedDateTime.ofInstant(expires, ZoneId.systemDefault());
}
return this;
}
/**
* Sets {@code Max-Age} parameter.
*
* @param maxAge an {@code Max-Age} parameter.
* @return Updated instance.
*/
public SetCookie maxAge(Duration maxAge) {
this.maxAge = maxAge;
return this;
}
/**
* Sets {@code Domain} parameter.
*
* @param domain an {@code Domain} parameter.
* @return Updated instance.
*/
public SetCookie domain(String domain) {
this.domain = domain;
return this;
}
/**
* Sets {@code Path} parameter.
*
* @param path an {@code Path} parameter.
* @return Updated instance.
*/
public SetCookie path(String path) {
this.path = path;
return this;
}
/**
* Sets {@code Domain} and {@code Path} parameters.
*
* @param domainAndPath an URI to specify {@code Domain} and {@code Path} parameters.
* @return Updated instance.
*/
public SetCookie domainAndPath(URI domainAndPath) {
if (domainAndPath == null) {
this.domain = null;
this.path = null;
} else {
this.domain = domainAndPath.getHost();
this.path = domainAndPath.getPath();
}
return this;
}
/**
* Sets {@code Secure} parameter.
*
* @param secure an {@code Secure} parameter.
* @return Updated instance.
*/
public SetCookie secure(boolean secure) {
this.secure = secure;
return this;
}
/**
* Sets {@code HttpOnly} parameter.
*
* @param httpOnly an {@code HttpOnly} parameter.
* @return Updated instance.
*/
public SetCookie httpOnly(boolean httpOnly) {
this.httpOnly = httpOnly;
return this;
}
/**
* Returns content of this instance as a 'Set-Cookie:' header value specified
* by <a href="https://tools.ietf.org/html/rfc6265">RFC6265</a>.
*
* @return a 'Set-Cookie:' header value.
*/
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(name).append('=').append(value);
if (expires != null) {
result.append(PARAM_SEPARATOR);
result.append("Expires=");
result.append(expires.format(Http.DateTime.RFC_1123_DATE_TIME));
}
if (maxAge != null && !maxAge.isNegative() && !maxAge.isZero()) {
result.append(PARAM_SEPARATOR);
result.append("Max-Age=");
result.append(maxAge.getSeconds());
}
if (domain != null) {
result.append(PARAM_SEPARATOR);
result.append("Domain=");
result.append(domain);
}
if (path != null) {
result.append(PARAM_SEPARATOR);
result.append("Path=");
result.append(path);
}
if (secure) {
result.append(PARAM_SEPARATOR);
result.append("Secure");
}
if (httpOnly) {
result.append(PARAM_SEPARATOR);
result.append("HttpOnly");
}
return result.toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.List;
import java.util.Map;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,42 +14,24 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
/**
* Internal utility methods.
*/
final class Utils {
public final class Utils {
static final Runnable EMPTY_RUNNABLE = () -> {
};
private Utils() {
}
/**
* Loads the first service implementation or throw an exception if nothing found.
*
* @param service the service class to load
* @param <T> service type
* @return the loaded service
* @throws IllegalStateException if none implementation found
*/
static <T> T loadSpi(Class<T> service) {
ServiceLoader<T> servers = ServiceLoader.load(service);
Iterator<T> serversIt = servers.iterator();
if (serversIt.hasNext()) {
return serversIt.next();
} else {
throw new IllegalStateException("No implementation found for SPI: " + service.getName());
}
}
/**
* Tokenize provide {@code text} by {@code separator} char respecting quoted sub-sequences. Quoted sub-sequences are
* parts of {@code text} which starts and ends by the same {@code quoteChar}.
@@ -61,7 +43,7 @@ final class Utils {
* @param text a text to be tokenized.
* @return A list of tokens without separator characters.
*/
static List<String> tokenize(char separator, String quoteChars, boolean includeEmptyTokens, String text) {
public static List<String> tokenize(char separator, String quoteChars, boolean includeEmptyTokens, String text) {
char[] quotes = quoteChars == null ? new char[0] : quoteChars.toCharArray();
StringBuilder token = new StringBuilder();
List<String> result = new ArrayList<>();
@@ -105,7 +87,7 @@ final class Utils {
* @param byteBuffer the byte buffer to append to the stream
* @throws IOException in case of an IO problem
*/
static void write(ByteBuffer byteBuffer, OutputStream out) throws IOException {
public static void write(ByteBuffer byteBuffer, OutputStream out) throws IOException {
if (byteBuffer.hasArray()) {
out.write(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), byteBuffer.remaining());
} else {
@@ -114,4 +96,16 @@ final class Utils {
out.write(buff);
}
}
static byte[] toByteArray(ByteBuffer byteBuffer) {
byte[] buff = new byte[byteBuffer.remaining()];
if (byteBuffer.hasArray()) {
System.arraycopy(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), buff, 0, buff.length);
} else {
byteBuffer.get(buff);
}
return buff;
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* HTTP APIs and implementations usable by both server and client side of the HTTP story.
*/
package io.helidon.common.http;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,22 +14,13 @@
* limitations under the License.
*/
package io.helidon.webserver;
/**
* This Builder should be implemented by all the builders in order to enable
* seamless exchange between the builder itself and the objects it's
* building when calling the methods. Such methods need to have overloads
* with builder parameters as well as concrete instances.
*
* @param <T> the type this builder is building
* Helidon Common classes for HTTP server and client.
*/
public interface Builder<T> {
module io.helidon.common.http {
requires java.logging;
requires io.helidon.common;
requires io.helidon.common.reactive;
/**
* Builds the object.
*
* @return the built object
*/
T build();
exports io.helidon.common.http;
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common.http;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Unit test for {@link DataChunk}.
*/
class DataChunkTest {
@Test
public void testSimpleWrapping() {
byte[] bytes = "urzatron".getBytes(StandardCharsets.UTF_8);
DataChunk chunk = DataChunk.create(bytes);
assertThat(chunk.bytes(), is(bytes));
assertThat(chunk.flush(), is(false));
assertThat(chunk.id(), not(0L));
assertThat(chunk.isReleased(), is(false));
assertThat(chunk.data().array(), is(bytes));
}
@Test
public void testReleasing() {
byte[] bytes = "urzatron".getBytes(StandardCharsets.UTF_8);
AtomicBoolean ab = new AtomicBoolean(false);
DataChunk chunk = DataChunk.create(true, ByteBuffer.wrap(bytes), () -> ab.set(true));
assertThat(chunk.bytes(), is(bytes));
assertThat(chunk.flush(), is(true));
assertThat(chunk.id(), not(0L));
assertThat(chunk.data().array(), is(bytes));
assertThat(chunk.isReleased(), is(false));
assertThat(ab.get(), is(false));
chunk.release();
assertThat(chunk.isReleased(), is(true));
assertThat(ab.get(), is(true));
}
@Test
public void testReleasingNoRunnable() {
byte[] bytes = "urzatron".getBytes(StandardCharsets.UTF_8);
DataChunk chunk = DataChunk.create(true, ByteBuffer.wrap(bytes));
assertThat(chunk.bytes(), is(bytes));
assertThat(chunk.flush(), is(true));
assertThat(chunk.id(), not(0L));
assertThat(chunk.data().array(), is(bytes));
assertThat(chunk.isReleased(), is(false));
chunk.release();
assertThat(chunk.isReleased(), is(true));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -22,7 +22,7 @@ import java.time.ZonedDateTime;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
/**
@@ -30,8 +30,8 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
*/
public class DateTimeTest {
public static final ZonedDateTime ZDT = ZonedDateTime.of(2008, 6, 3, 11, 5, 30, 0, ZoneId.of("Z"));
public static final ZonedDateTime ZDT2 = ZonedDateTime.of(2008, 6, 17, 11, 5, 30, 0, ZoneId.of("Z"));
private static final ZonedDateTime ZDT = ZonedDateTime.of(2008, 6, 3, 11, 5, 30, 0, ZoneId.of("Z"));
private static final ZonedDateTime ZDT2 = ZonedDateTime.of(2008, 6, 17, 11, 5, 30, 0, ZoneId.of("Z"));
@Test
public void rfc1123() throws Exception {

View File

@@ -14,14 +14,16 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* Tests {@link ListContextualRegistry} and {@link ContextualRegistry}.

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common.http;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Unit test for {@link MediaType}.
*/
class MediaTypeTest {
@Test
void testText() {
MediaType textPlain = MediaType.TEXT_PLAIN;
assertThat(textPlain.getType(), is("text"));
assertThat(textPlain.getSubtype(), is("plain"));
assertThat(textPlain.getCharset(), is(Optional.empty()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,23 +14,26 @@
* limitations under the License.
*/
package io.helidon.webserver;
package io.helidon.common.http;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Tests {@link Utils}.
*/
public class UtilsTest {
class UtilsTest {
@Test
public void tokenize() throws Exception {
void tokenize() throws Exception {
String text = ",aa,,fo\"oooo\",\"bar\",co\"o'l,e\"c,df'hk,lm',";
List<String> tokens = Utils.tokenize(',', null, false, text);
assertThat(tokens, contains("aa", "fo\"oooo\"", "\"bar\"", "co\"o'l", "e\"c", "df'hk", "lm'"));
@@ -48,4 +51,13 @@ public class UtilsTest {
assertEquals(1, tokens.size());
assertEquals(text, tokens.get(0));
}
@Test
void testByteBufferToByteArray() {
byte[] array = "halleluja".getBytes(StandardCharsets.UTF_8);
ByteBuffer wrap = ByteBuffer.wrap(array);
byte[] unwrapped = Utils.toByteArray(wrap);
assertThat(unwrapped, is(array));
}
}

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-common-key-util</artifactId>
<name>Helidon Common Key Util</name>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon</groupId>
<artifactId>helidon-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-project</artifactId>
@@ -35,5 +35,6 @@
<module>reactive</module>
<module>configurable</module>
<module>key-util</module>
<module>http</module>
</modules>
</project>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-common-reactive</artifactId>
<name>Helidon Common Reactive</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-bundle</artifactId>
<name>Helidon Config Bundle</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config</artifactId>
<name>Helidon Config</name>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-etcd</artifactId>
<name>Helidon Config Etcd</name>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config.examples</groupId>
<artifactId>helidon-config-examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-examples-basics</artifactId>
<name>Helidon Config Examples Basics</name>
@@ -33,7 +33,7 @@
</description>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<example.mainClass>io.helidon.config.examples.basics.Main</example.mainClass>
</properties>

View File

@@ -24,7 +24,7 @@
<parent>
<artifactId>helidon-config-examples-project</artifactId>
<groupId>io.helidon.config.examples</groupId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-examples-changes</artifactId>
<name>Helidon Config Examples Changes</name>
@@ -34,7 +34,7 @@
</description>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<example.mainClass>io.helidon.config.examples.changes.Main</example.mainClass>
</properties>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config.examples</groupId>
<artifactId>helidon-config-examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-examples-git</artifactId>
<name>Helidon Config Examples Git</name>
@@ -32,7 +32,7 @@
</description>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<example.mainClass>io.helidon.config.examples.git.Main</example.mainClass>
</properties>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config.examples</groupId>
<artifactId>helidon-config-examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-examples-mapping</artifactId>
<name>Helidon Config Examples Mapping</name>
@@ -33,7 +33,7 @@
</description>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<example.mainClass>io.helidon.config.examples.mapping.Main</example.mainClass>
</properties>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config.examples</groupId>
<artifactId>helidon-config-examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-examples-overrides</artifactId>
<name>Helidon Config Examples Overrides</name>
@@ -33,7 +33,7 @@
</description>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<example.mainClass>io.helidon.config.examples.overrides.Main</example.mainClass>
</properties>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<groupId>io.helidon.config.examples</groupId>
<artifactId>helidon-config-examples-project</artifactId>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config.examples</groupId>
<artifactId>helidon-config-examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-examples-sources</artifactId>
<name>Helidon Config Examples Sources</name>
@@ -33,7 +33,7 @@
</description>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<example.mainClass>io.helidon.config.examples.sources.Main</example.mainClass>
</properties>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-git</artifactId>
<name>Helidon Config Git</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-hocon</artifactId>
<name>Helidon Config HOCON</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon</groupId>
<artifactId>helidon-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-test-infrastructure</artifactId>
<name>Helidon Config Test Infrastructure</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-testing</artifactId>
<name>Helidon Config Testing</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-integration-tests</artifactId>
<name>Helidon Config Tests Integration</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-module-mappers-1-base</artifactId>
<name>Helidon Config Tests Mappers 1</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-module-mappers-2-override</artifactId>
<name>Helidon Config Tests Parser 2</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-module-meta-source-1</artifactId>
<name>Helidon Config Tests Meta Source 1</name>

View File

@@ -25,7 +25,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-module-meta-source-2</artifactId>
<name>Helidon Config Tests Meta Source 2</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-module-parsers-1-override</artifactId>
<name>Helidon Config Tests Parser 1</name>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-bundle</artifactId>
<name>Helidon Config Tests Bundle</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-1-properties</artifactId>
<name>Helidon Config Tests Default Config 1</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-2-hocon-json</artifactId>
<name>Helidon Config Tests Default Config 2</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-3-hocon</artifactId>
<name>Helidon Config Tests Default Config 3</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-4-yaml</artifactId>
<name>Helidon Config Tests Default Config 4</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-5-env_vars</artifactId>
<name>Helidon Config Tests Default Config 5</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-6-meta-properties</artifactId>
<name>Helidon Config Tests Default Config 6</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-7-meta-hocon-json</artifactId>
<name>Helidon Config Tests Default Config 7</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-8-meta-hocon</artifactId>
<name>Helidon Config Tests Default Config 8</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-default_config-9-meta-yaml</artifactId>
<name>Helidon Config Tests Default Config 9</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-mappers-1-common</artifactId>
<name>Helidon Config Tests Mappers Common 1</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-mappers-2-complex</artifactId>
<name>Helidon Config Tests Parsers 2</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-meta-source</artifactId>
<name>Helidon Config Tests Meta Source</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config.tests</groupId>
<artifactId>helidon-config-tests-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-tests-test-parsers-1-complex</artifactId>
<name>Helidon Config Tests Parsers 1</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-config-yaml</artifactId>
<name>Helidon Config YAML</name>

View File

@@ -22,7 +22,7 @@
<parent>
<groupId>io.helidon</groupId>
<artifactId>helidon-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-docs</artifactId>
<name>Helidon Documentation</name>

View File

@@ -19,6 +19,6 @@
archetype.groupId=io.helidon.archetypes
# Expanded by create-archetypes.sh script
archetype.artifactId=${archetypeArtifactId}
archetype.version=0.9.2-SNAPSHOT
archetype.version=0.10.0-SNAPSHOT
archetype.languages=java, docker
excludePatterns=**/.idea/**, *.iml, target/**, build/**, build.gradle, settings.gradle, .gradle/**

View File

@@ -29,7 +29,7 @@ tasks.withType(JavaCompile) {
}
ext {
helidonversion = '0.9.2-SNAPSHOT'
helidonversion = '0.10.0-SNAPSHOT'
}
repositories {

View File

@@ -19,18 +19,17 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://mav
en.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
>
<modelVersion>4.0.0</modelVersion>
<groupId>io.helidon.examples</groupId>
<artifactId>quickstart-mp</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<!-- Default package. Will be overriden by Maven archetype -->
<package>io.helidon.examples.quickstart.mp</package>
<mainClass>${package}.Main</mainClass>

View File

@@ -29,7 +29,7 @@ tasks.withType(JavaCompile) {
}
ext {
helidonversion = '0.9.2-SNAPSHOT'
helidonversion = '0.10.0-SNAPSHOT'
}
repositories {

View File

@@ -19,18 +19,17 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://mav
en.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
>
<modelVersion>4.0.0</modelVersion>
<groupId>io.helidon.examples</groupId>
<artifactId>quickstart-se</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<properties>
<helidon.version>0.9.2-SNAPSHOT</helidon.version>
<helidon.version>0.10.0-SNAPSHOT</helidon.version>
<!-- Default package. Will be overriden by Maven archetype -->
<package>io.helidon.examples.quickstart.se</package>
<mainClass>${package}.Main</mainClass>

View File

@@ -23,7 +23,7 @@
<parent>
<groupId>io.helidon</groupId>
<artifactId>helidon-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<groupId>io.helidon.examples</groupId>
<artifactId>helidon-examples-project</artifactId>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.demo.todos</groupId>
<artifactId>helidon-demo-todos-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-todos-backend</artifactId>
<name>Helidon Examples TODOs Demo Backend</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.demo.todos</groupId>
<artifactId>helidon-demo-todos-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-todos-frontend</artifactId>
<name>Helidon Examples TODOs Demo Frontend</name>

View File

@@ -30,9 +30,9 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import io.helidon.common.CollectionsHelper;
import io.helidon.common.http.Http;
import io.helidon.config.Config;
import io.helidon.security.SecurityContext;
import io.helidon.webserver.Http;
import io.helidon.webserver.ServerResponse;
import io.opentracing.Span;

View File

@@ -22,9 +22,9 @@ import java.util.function.Consumer;
import javax.json.JsonObject;
import io.helidon.common.OptionalHelper;
import io.helidon.common.http.Http;
import io.helidon.metrics.RegistryFactory;
import io.helidon.security.SecurityContext;
import io.helidon.webserver.Http;
import io.helidon.webserver.Routing;
import io.helidon.webserver.ServerRequest;
import io.helidon.webserver.ServerResponse;
@@ -78,6 +78,7 @@ public final class TodosHandler implements Service {
/**
* Create a new {@code TodosHandler} instance.
*
* @param bsc the {@code BackendServiceClient} to use
*/
public TodosHandler(BackendServiceClient bsc) {
@@ -109,6 +110,7 @@ public final class TodosHandler implements Service {
/**
* Handler for {@code POST /todo}.
*
* @param req the server request
* @param res the server response
*/
@@ -116,12 +118,13 @@ public final class TodosHandler implements Service {
secure(req, res, sc -> json(req, res, json -> {
createCounter.inc();
sendResponse(res, bsc.create(sc, req.spanContext(), json),
Http.Status.INTERNAL_SERVER_ERROR_500);
Http.Status.INTERNAL_SERVER_ERROR_500);
}));
}
/**
* Handler for {@code GET /todo}.
*
* @param req the server request
* @param res the server response
*/
@@ -131,6 +134,7 @@ public final class TodosHandler implements Service {
/**
* Handler for {@code PUT /todo/id}.
*
* @param req the server request
* @param res the server response
*/
@@ -139,12 +143,13 @@ public final class TodosHandler implements Service {
updateCounter.inc();
// example of asynchronous processing
bsc.update(sc, req.spanContext(),
req.path().param("id"), json, res);
req.path().param("id"), json, res);
}));
}
/**
* Handler for {@code DELETE /todo/id}.
*
* @param req the server request
* @param res the server response
*/
@@ -152,28 +157,30 @@ public final class TodosHandler implements Service {
secure(req, res, sc -> {
deleteCounter.inc();
sendResponse(res,
bsc.deleteSingle(sc, req.spanContext(),
req.path().param("id")),
Http.Status.NOT_FOUND_404);
bsc.deleteSingle(sc, req.spanContext(),
req.path().param("id")),
Http.Status.NOT_FOUND_404);
});
}
/**
* Handler for {@code GET /todo/id}.
*
* @param req the server request
* @param res the server response
*/
private void getSingle(final ServerRequest req, final ServerResponse res) {
secure(req, res, sc -> {
sendResponse(res,
bsc.getSingle(sc, req.spanContext(),
req.path().param("id")),
Http.Status.NOT_FOUND_404);
bsc.getSingle(sc, req.spanContext(),
req.path().param("id")),
Http.Status.NOT_FOUND_404);
});
}
/**
* Send a response with a {@code 500} status code.
*
* @param res the server response
*/
private void noSecurityContext(final ServerResponse res) {
@@ -184,8 +191,9 @@ public final class TodosHandler implements Service {
/**
* Send the response entity if {@code jsonResponse} has a value, otherwise
* sets the status to {@code failureStatus}.
* @param res the server response
* @param jsonResponse the response entity
*
* @param res the server response
* @param jsonResponse the response entity
* @param failureStatus the status to use if {@code jsonResponse} is empty
*/
private void sendResponse(final ServerResponse res,
@@ -200,8 +208,9 @@ public final class TodosHandler implements Service {
* Reads a request entity as {@JsonObject}, and if successful invoke the
* given consumer, otherwise terminate the request with a {@code 500}
* status code.
* @param req the server request
* @param res the server response
*
* @param req the server request
* @param res the server response
* @param json the {@code JsonObject} consumer
*/
private void json(final ServerRequest req,
@@ -214,7 +223,7 @@ public final class TodosHandler implements Service {
.exceptionally(throwable -> {
res.status(Http.Status.INTERNAL_SERVER_ERROR_500);
res.send(throwable.getClass().getName()
+ ": " + throwable.getMessage());
+ ": " + throwable.getMessage());
return null;
});
}
@@ -223,6 +232,7 @@ public final class TodosHandler implements Service {
* Reads the request security context, and if successful invoke the given
* consumer, otherwise terminate the request with a {@code 500}
* status code.
*
* @param req the server request
* @param res the server response
* @param ctx the {@code SecurityContext} consumer
@@ -232,7 +242,7 @@ public final class TodosHandler implements Service {
final Consumer<SecurityContext> ctx) {
OptionalHelper.from(req.context()
.get(SecurityContext.class))
.get(SecurityContext.class))
.ifPresentOrElse(ctx, () -> noSecurityContext(res));
}
}

View File

@@ -25,7 +25,7 @@
<groupId>io.helidon.demo.todos</groupId>
<artifactId>helidon-demo-todos-project</artifactId>
<packaging>pom</packaging>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
<name>Helidon Examples TODOs Demo</name>
<description>

View File

@@ -22,7 +22,7 @@
<parent>
<groupId>io.helidon</groupId>
<artifactId>helidon-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-javadocs</artifactId>
<name>Helidon Javadocs</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>bundles-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-microprofile-1.0</artifactId>
<name>Helidon Microprofile Bundles 1.0</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>bundles-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-microprofile-1.1</artifactId>
<name>Helidon Microprofile Bundles 1.1</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>bundles-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-microprofile-1.2</artifactId>
<name>Helidon Microprofile Bundles 1.2</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>bundles-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>internal-test-libs</artifactId>
<name>Helidon Microprofile Bundles Test Libraries</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<packaging>pom</packaging>
<groupId>io.helidon.microprofile.bundles</groupId>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.config</groupId>
<artifactId>helidon-microprofile-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-microprofile-config-cdi</artifactId>
<name>Helidon Microprofile Config CDI</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.config</groupId>
<artifactId>helidon-microprofile-config-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-microprofile-config</artifactId>
<name>Helidon Microprofile Config Core</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<packaging>pom</packaging>
<groupId>io.helidon.microprofile.config</groupId>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.examples</groupId>
<artifactId>examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>hello-world-explicit</artifactId>
<name>Helidon Microprofile Examples Explicit Hello World</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.examples</groupId>
<artifactId>examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>hello-world-implicit</artifactId>
<name>Helidon Microprofile Examples Implicit Hello World</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.examples</groupId>
<artifactId>examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>security-idcs</artifactId>
<name>Helidon Microprofile Examples IDCS Security</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.examples</groupId>
<artifactId>examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>mp1_1-security</artifactId>
<name>Helidon Microprofile Examples MP 1.1 Security</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.examples</groupId>
<artifactId>examples-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>mp1_1-static-content</artifactId>
<name>Helidon Microprofile Examples MP 1.1 Static Content</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<packaging>pom</packaging>
<groupId>io.helidon.microprofile.examples</groupId>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.health</groupId>
<artifactId>helidon-microprofile-health-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-microprofile-health-checks</artifactId>
<name>Helidon Microprofile Health Checks</name>

View File

@@ -24,7 +24,7 @@
<parent>
<groupId>io.helidon.microprofile.health</groupId>
<artifactId>helidon-microprofile-health-project</artifactId>
<version>0.9.2-SNAPSHOT</version>
<version>0.10.0-SNAPSHOT</version>
</parent>
<artifactId>helidon-microprofile-health-core</artifactId>
<name>Helidon Microprofile Health Core</name>

Some files were not shown because too many files have changed in this diff Show More