Add caching and improve performance
Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/GuardianStringKeyMultivaluedMap.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/GuardianStringKeyMultivaluedMap.java
new file mode 100644
index 0000000..b2605fe
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/GuardianStringKeyMultivaluedMap.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.internal.util.collection;
+
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * The {@link MultivaluedMap} wrapper that is able to set guards observing changes of values represented by a key.
+ * @param <V> The value type of the wrapped {@code MultivaluedMap}.
+ *
+ * @since 2.38
+ */
+public class GuardianStringKeyMultivaluedMap<V> implements MultivaluedMap<String, V> {
+
+ private final MultivaluedMap<String, V> inner;
+ private final Map<String, Boolean> guards = new HashMap<>();
+
+ public GuardianStringKeyMultivaluedMap(MultivaluedMap<String, V> inner) {
+ this.inner = inner;
+ }
+
+ @Override
+ public void putSingle(String key, V value) {
+ observe(key);
+ inner.putSingle(key, value);
+ }
+
+ @Override
+ public void add(String key, V value) {
+ observe(key);
+ inner.add(key, value);
+ }
+
+ @Override
+ public V getFirst(String key) {
+ return inner.getFirst(key);
+ }
+
+ @Override
+ public void addAll(String key, V... newValues) {
+ observe(key);
+ inner.addAll(key, newValues);
+ }
+
+ @Override
+ public void addAll(String key, List<V> valueList) {
+ observe(key);
+ inner.addAll(key, valueList);
+ }
+
+ @Override
+ public void addFirst(String key, V value) {
+ observe(key);
+ inner.addFirst(key, value);
+ }
+
+ @Override
+ public boolean equalsIgnoreValueOrder(MultivaluedMap<String, V> otherMap) {
+ return inner.equalsIgnoreValueOrder(otherMap);
+ }
+
+ @Override
+ public int size() {
+ return inner.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return inner.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return inner.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return inner.containsValue(value);
+ }
+
+ @Override
+ public List<V> get(Object key) {
+ return inner.get(key);
+ }
+
+ @Override
+ public List<V> put(String key, List<V> value) {
+ observe(key);
+ return inner.put(key, value);
+ }
+
+ @Override
+ public List<V> remove(Object key) {
+ if (key != null) {
+ observe(key.toString());
+ }
+ return inner.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends String, ? extends List<V>> m) {
+ for (String key : m.keySet()) {
+ observe(key);
+ }
+ inner.putAll(m);
+ }
+
+ @Override
+ public void clear() {
+ observeAll();
+ inner.clear();
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return inner.keySet();
+ }
+
+ @Override
+ public Collection<List<V>> values() {
+ return inner.values();
+ }
+
+ @Override
+ public Set<Entry<String, List<V>>> entrySet() {
+ return inner.entrySet();
+ }
+
+ /**
+ * Observe changes of a value represented by the key.
+ * @param key the key values to observe
+ */
+ public void setGuard(String key) {
+ guards.put(key, false);
+ }
+
+ /**
+ * Get all the guarded keys
+ * @return a {@link Set} of keys guarded.
+ */
+ public Set<String> getGuards() {
+ return guards.keySet();
+ }
+
+ /**
+ * Return true when the value represented by the key has changed. Resets any observation - the operation is not idempotent.
+ * @param key the Key observed.
+ * @return whether the value represented by the key has changed.
+ */
+ public boolean isObservedAndReset(String key) {
+ Boolean observed = guards.get(key);
+ guards.put(key, false);
+ return observed != null && observed;
+ }
+
+ private void observe(String key) {
+ for (Map.Entry<String, Boolean> guard : guards.entrySet()) {
+ if (guard.getKey().equals(key)) {
+ guard.setValue(true);
+ }
+ }
+ }
+
+ private void observeAll() {
+ for (Map.Entry<String, Boolean> guard : guards.entrySet()) {
+ guard.setValue(true);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return inner.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ GuardianStringKeyMultivaluedMap<?> that = (GuardianStringKeyMultivaluedMap<?>) o;
+ return inner.equals(that.inner) && guards.equals(that.guards);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(inner, guards);
+ }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/LRU.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/LRU.java
new file mode 100644
index 0000000..3338b00
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/LRU.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.internal.util.collection;
+
+import org.glassfish.jersey.internal.guava.Cache;
+import org.glassfish.jersey.internal.guava.CacheBuilder;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An abstract LRU interface wrapping an actual LRU implementation.
+ * @param <K> Key type
+ * @param <V> Value type
+ * @Since 2.38
+ */
+public abstract class LRU<K, V> {
+
+ /**
+ * Returns the value associated with {@code key} in this cache, or {@code null} if there is no
+ * cached value for {@code key}.
+ */
+ public abstract V getIfPresent(Object key);
+
+ /**
+ * Associates {@code value} with {@code key} in this cache. If the cache previously contained a
+ * value associated with {@code key}, the old value is replaced by {@code value}.
+ */
+ public abstract void put(K key, V value);
+
+ /**
+ * Create new LRU
+ * @return new LRU
+ */
+ public static <K, V> LRU<K, V> create() {
+ return LRUFactory.createLRU();
+ }
+
+ private static class LRUFactory {
+ // TODO configure via the Configuration
+ public static final int LRU_CACHE_SIZE = 128;
+ public static final long TIMEOUT = 5000L;
+ private static <K, V> LRU<K, V> createLRU() {
+ final Cache<K, V> CACHE = CacheBuilder.newBuilder()
+ .maximumSize(LRU_CACHE_SIZE)
+ .expireAfterAccess(TIMEOUT, TimeUnit.MILLISECONDS)
+ .build();
+ return new LRU<K, V>() {
+ @Override
+ public V getIfPresent(Object key) {
+ return CACHE.getIfPresent(key);
+ }
+
+ @Override
+ public void put(K key, V value) {
+ CACHE.put(key, value);
+ }
+ };
+ }
+ }
+
+
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
index 3c6cfdd..5b47d6b 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -102,7 +102,7 @@
* if the supplied header value is {@code null}.
*/
@SuppressWarnings("unchecked")
- private static String asString(final Object headerValue, RuntimeDelegate rd) {
+ public static String asString(final Object headerValue, RuntimeDelegate rd) {
if (headerValue == null) {
return null;
}
@@ -149,7 +149,7 @@
* will be called for before element conversion.
* @return String view of header values.
*/
- private static List<String> asStringList(final List<Object> headerValues, final RuntimeDelegate rd) {
+ public static List<String> asStringList(final List<Object> headerValues, final RuntimeDelegate rd) {
if (headerValues == null || headerValues.isEmpty()) {
return Collections.emptyList();
}
@@ -191,7 +191,24 @@
return null;
}
- final RuntimeDelegate rd = RuntimeDelegateDecorator.configured(configuration);
+ return asStringHeaders(headers, RuntimeDelegateDecorator.configured(configuration));
+ }
+
+ /**
+ * Returns string view of passed headers. Any modifications to the headers are visible to the view, the view also
+ * supports removal of elements. Does not support other modifications.
+ *
+ * @param headers headers.
+ * @param rd {@link RuntimeDelegate} instance or {@code null} (in that case {@link RuntimeDelegate#getInstance()}
+ * will be called for before conversion of elements).
+ * @return String view of headers or {@code null} if {code headers} input parameter is {@code null}.
+ */
+ public static MultivaluedMap<String, String> asStringHeaders(
+ final MultivaluedMap<String, Object> headers, RuntimeDelegate rd) {
+ if (headers == null) {
+ return null;
+ }
+
return new AbstractMultivaluedMap<String, String>(
Views.mapView(headers, input -> HeaderUtils.asStringList(input, rd))
) {
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
index dacd743..a2f9eeb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,6 +23,7 @@
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -31,6 +32,7 @@
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
+import org.glassfish.jersey.internal.util.collection.LRU;
/**
* An abstract pull-based reader of HTTP headers.
@@ -371,61 +373,25 @@
return l;
}
- private static final ListElementCreator<MediaType> MEDIA_TYPE_CREATOR =
- new ListElementCreator<MediaType>() {
-
- @Override
- public MediaType create(HttpHeaderReader reader) throws ParseException {
- return MediaTypeProvider.valueOf(reader);
- }
- };
-
/**
* TODO javadoc.
*/
public static List<MediaType> readMediaTypes(List<MediaType> l, String header) throws ParseException {
- return HttpHeaderReader.readList(
- l,
- MEDIA_TYPE_CREATOR,
- header);
+ return MEDIA_TYPE_LIST_READER.readList(l, header);
}
- private static final ListElementCreator<AcceptableMediaType> ACCEPTABLE_MEDIA_TYPE_CREATOR =
- new ListElementCreator<AcceptableMediaType>() {
-
- @Override
- public AcceptableMediaType create(HttpHeaderReader reader) throws ParseException {
- return AcceptableMediaType.valueOf(reader);
- }
- };
-
/**
* TODO javadoc.
*/
public static List<AcceptableMediaType> readAcceptMediaType(String header) throws ParseException {
- return HttpHeaderReader.readQualifiedList(
- AcceptableMediaType.COMPARATOR,
- ACCEPTABLE_MEDIA_TYPE_CREATOR,
- header);
+ return ACCEPTABLE_MEDIA_TYPE_LIST_READER.readList(header);
}
- private static final ListElementCreator<QualitySourceMediaType> QUALITY_SOURCE_MEDIA_TYPE_CREATOR =
- new ListElementCreator<QualitySourceMediaType>() {
-
- @Override
- public QualitySourceMediaType create(HttpHeaderReader reader) throws ParseException {
- return QualitySourceMediaType.valueOf(reader);
- }
- };
-
/**
* FIXME use somewhere in production code or remove.
*/
public static List<QualitySourceMediaType> readQualitySourceMediaType(String header) throws ParseException {
- return HttpHeaderReader.readQualifiedList(
- QualitySourceMediaType.COMPARATOR,
- QUALITY_SOURCE_MEDIA_TYPE_CREATOR,
- header);
+ return QUALITY_SOURCE_MEDIA_TYPE_LIST_READER.readList(header);
}
/**
@@ -454,121 +420,246 @@
public static List<AcceptableMediaType> readAcceptMediaType(
final String header, final List<QualitySourceMediaType> priorityMediaTypes) throws ParseException {
- return HttpHeaderReader.readQualifiedList(
- new Comparator<AcceptableMediaType>() {
-
- @Override
- public int compare(AcceptableMediaType o1, AcceptableMediaType o2) {
- // FIXME what is going on here?
- boolean q_o1_set = false;
- int q_o1 = 0;
- boolean q_o2_set = false;
- int q_o2 = 0;
- for (QualitySourceMediaType priorityType : priorityMediaTypes) {
- if (!q_o1_set && MediaTypes.typeEqual(o1, priorityType)) {
- q_o1 = o1.getQuality() * priorityType.getQuality();
- q_o1_set = true;
- } else if (!q_o2_set && MediaTypes.typeEqual(o2, priorityType)) {
- q_o2 = o2.getQuality() * priorityType.getQuality();
- q_o2_set = true;
- }
- }
- int i = q_o2 - q_o1;
- if (i != 0) {
- return i;
- }
-
- i = o2.getQuality() - o1.getQuality();
- if (i != 0) {
- return i;
- }
-
- return MediaTypes.PARTIAL_ORDER_COMPARATOR.compare(o1, o2);
- }
- },
- ACCEPTABLE_MEDIA_TYPE_CREATOR,
- header);
+ return new AcceptMediaTypeListReader(priorityMediaTypes).readList(header);
}
- private static final ListElementCreator<AcceptableToken> ACCEPTABLE_TOKEN_CREATOR =
- new ListElementCreator<AcceptableToken>() {
-
- @Override
- public AcceptableToken create(HttpHeaderReader reader) throws ParseException {
- return new AcceptableToken(reader);
- }
- };
/**
* TODO javadoc.
*/
public static List<AcceptableToken> readAcceptToken(String header) throws ParseException {
- return HttpHeaderReader.readQualifiedList(ACCEPTABLE_TOKEN_CREATOR, header);
+ return ACCEPTABLE_TOKEN_LIST_READER.readList(header);
}
- private static final ListElementCreator<AcceptableLanguageTag> LANGUAGE_CREATOR =
- new ListElementCreator<AcceptableLanguageTag>() {
-
- @Override
- public AcceptableLanguageTag create(HttpHeaderReader reader) throws ParseException {
- return new AcceptableLanguageTag(reader);
- }
- };
-
/**
* TODO javadoc.
*/
public static List<AcceptableLanguageTag> readAcceptLanguage(String header) throws ParseException {
- return HttpHeaderReader.readQualifiedList(LANGUAGE_CREATOR, header);
- }
-
- private static <T extends Qualified> List<T> readQualifiedList(ListElementCreator<T> c, String header)
- throws ParseException {
-
- List<T> l = readList(c, header);
- Collections.sort(l, Quality.QUALIFIED_COMPARATOR);
- return l;
- }
-
- private static <T> List<T> readQualifiedList(final Comparator<T> comparator, ListElementCreator<T> c, String header)
- throws ParseException {
-
- List<T> l = readList(c, header);
- Collections.sort(l, comparator);
- return l;
+ return ACCEPTABLE_LANGUAGE_TAG_LIST_READER.readList(header);
}
/**
* TODO javadoc.
*/
public static List<String> readStringList(String header) throws ParseException {
- return readList(new ListElementCreator<String>() {
+ return STRING_LIST_READER.readList(header);
+ }
+ private static final MediaTypeListReader MEDIA_TYPE_LIST_READER = new MediaTypeListReader();
+ private static final AcceptableMediaTypeListReader ACCEPTABLE_MEDIA_TYPE_LIST_READER = new AcceptableMediaTypeListReader();
+ private static final QualitySourceMediaTypeListReader QUALITY_SOURCE_MEDIA_TYPE_LIST_READER =
+ new QualitySourceMediaTypeListReader();
+ private static final AcceptableTokenListReader ACCEPTABLE_TOKEN_LIST_READER = new AcceptableTokenListReader();
+ private static final AcceptableLanguageTagListReader ACCEPTABLE_LANGUAGE_TAG_LIST_READER =
+ new AcceptableLanguageTagListReader();
+ private static final StringListReader STRING_LIST_READER = new StringListReader();
+
+ private static class MediaTypeListReader extends ListReader<MediaType> {
+ private static final ListElementCreator<MediaType> MEDIA_TYPE_CREATOR =
+ new ListElementCreator<MediaType>() {
+
+ @Override
+ public MediaType create(HttpHeaderReader reader) throws ParseException {
+ return MediaTypeProvider.valueOf(reader);
+ }
+ };
+
+ List<MediaType> readList(List<MediaType> l, final String header) throws ParseException {
+ return super.readList(l, header);
+ }
+
+ private MediaTypeListReader() {
+ super(MEDIA_TYPE_CREATOR);
+ }
+ }
+
+ private static class AcceptableMediaTypeListReader extends QualifiedListReader<AcceptableMediaType> {
+ private static final ListElementCreator<AcceptableMediaType> ACCEPTABLE_MEDIA_TYPE_CREATOR =
+ new ListElementCreator<AcceptableMediaType>() {
+
+ @Override
+ public AcceptableMediaType create(HttpHeaderReader reader) throws ParseException {
+ return AcceptableMediaType.valueOf(reader);
+ }
+ };
+ private AcceptableMediaTypeListReader() {
+ super(ACCEPTABLE_MEDIA_TYPE_CREATOR, AcceptableMediaType.COMPARATOR);
+ }
+ }
+ /*
+ * TODO not used in production?
+ */
+ private static class QualitySourceMediaTypeListReader extends QualifiedListReader<QualitySourceMediaType> {
+ private static final ListElementCreator<QualitySourceMediaType> QUALITY_SOURCE_MEDIA_TYPE_CREATOR =
+ new ListElementCreator<QualitySourceMediaType>() {
+
+ @Override
+ public QualitySourceMediaType create(HttpHeaderReader reader) throws ParseException {
+ return QualitySourceMediaType.valueOf(reader);
+ }
+ };
+ private QualitySourceMediaTypeListReader() {
+ super(QUALITY_SOURCE_MEDIA_TYPE_CREATOR, QualitySourceMediaType.COMPARATOR);
+ }
+ }
+
+ /*
+ * TODO this is used in tests only
+ */
+ private static class AcceptMediaTypeListReader extends QualifiedListReader<AcceptableMediaType> {
+ AcceptMediaTypeListReader(List<QualitySourceMediaType> priorityMediaTypes) {
+ super(ACCEPTABLE_MEDIA_TYPE_CREATOR, new AcceptableMediaTypeComparator(priorityMediaTypes));
+ }
+
+ private static final ListElementCreator<AcceptableMediaType> ACCEPTABLE_MEDIA_TYPE_CREATOR =
+ new ListElementCreator<AcceptableMediaType>() {
+
+ @Override
+ public AcceptableMediaType create(HttpHeaderReader reader) throws ParseException {
+ return AcceptableMediaType.valueOf(reader);
+ }
+ };
+
+ private static class AcceptableMediaTypeComparator implements Comparator<AcceptableMediaType> {
+ private final List<QualitySourceMediaType> priorityMediaTypes;
+
+ private AcceptableMediaTypeComparator(List<QualitySourceMediaType> priorityMediaTypes) {
+ this.priorityMediaTypes = priorityMediaTypes;
+ }
+
+ @Override
+ public int compare(AcceptableMediaType o1, AcceptableMediaType o2) {
+ // FIXME what is going on here?
+ boolean q_o1_set = false;
+ int q_o1 = 0;
+ boolean q_o2_set = false;
+ int q_o2 = 0;
+ for (QualitySourceMediaType priorityType : priorityMediaTypes) {
+ if (!q_o1_set && MediaTypes.typeEqual(o1, priorityType)) {
+ q_o1 = o1.getQuality() * priorityType.getQuality();
+ q_o1_set = true;
+ } else if (!q_o2_set && MediaTypes.typeEqual(o2, priorityType)) {
+ q_o2 = o2.getQuality() * priorityType.getQuality();
+ q_o2_set = true;
+ }
+ }
+ int i = q_o2 - q_o1;
+ if (i != 0) {
+ return i;
+ }
+
+ i = o2.getQuality() - o1.getQuality();
+ if (i != 0) {
+ return i;
+ }
+
+ return MediaTypes.PARTIAL_ORDER_COMPARATOR.compare(o1, o2);
+ }
+ };
+
+
+ }
+
+ private static class AcceptableTokenListReader extends QualifiedListReader<AcceptableToken> {
+ private static final ListElementCreator<AcceptableToken> ACCEPTABLE_TOKEN_CREATOR =
+ new ListElementCreator<AcceptableToken>() {
+
+ @Override
+ public AcceptableToken create(HttpHeaderReader reader) throws ParseException {
+ return new AcceptableToken(reader);
+ }
+ };
+ private AcceptableTokenListReader() {
+ super(ACCEPTABLE_TOKEN_CREATOR);
+ }
+ }
+
+ private static class AcceptableLanguageTagListReader extends QualifiedListReader<AcceptableLanguageTag> {
+ private static final ListElementCreator<AcceptableLanguageTag> LANGUAGE_CREATOR =
+ new ListElementCreator<AcceptableLanguageTag>() {
+
+ @Override
+ public AcceptableLanguageTag create(HttpHeaderReader reader) throws ParseException {
+ return new AcceptableLanguageTag(reader);
+ }
+ };
+ private AcceptableLanguageTagListReader() {
+ super(LANGUAGE_CREATOR);
+ }
+ }
+
+ private abstract static class QualifiedListReader<T extends Qualified> extends ListReader<T> {
+ @Override
+ public List<T> readList(String header) throws ParseException {
+ List<T> l = super.readList(header);
+ Collections.sort(l, comparator);
+ return l;
+ }
+
+ private final Comparator<T> comparator;
+ private QualifiedListReader(ListElementCreator<T> creator) {
+ this(creator, (Comparator<T>) Quality.QUALIFIED_COMPARATOR);
+ }
+
+ protected QualifiedListReader(ListElementCreator<T> creator, Comparator<T> comparator) {
+ super(creator);
+ this.comparator = comparator;
+ }
+ }
+
+ private static class StringListReader extends ListReader<String> {
+ private static final ListElementCreator<String> listElementCreator = new ListElementCreator<String>() {
@Override
public String create(HttpHeaderReader reader) throws ParseException {
reader.hasNext();
return reader.nextToken().toString();
}
- }, header);
+ };
+
+ private StringListReader() {
+ super(listElementCreator);
+ }
}
- private static <T> List<T> readList(final ListElementCreator<T> c, final String header) throws ParseException {
- return readList(new ArrayList<T>(), c, header);
- }
+ private abstract static class ListReader<T> {
+ private final LRU<String, List<T>> LIST_CACHE = LRU.create();
+ protected final ListElementCreator<T> creator;
- private static <T> List<T> readList(final List<T> l, final ListElementCreator<T> c, final String header)
- throws ParseException {
-
- HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
- HttpHeaderListAdapter adapter = new HttpHeaderListAdapter(reader);
-
- while (reader.hasNext()) {
- l.add(c.create(adapter));
- adapter.reset();
- if (reader.hasNext()) {
- reader.next();
- }
+ protected ListReader(ListElementCreator<T> creator) {
+ this.creator = creator;
}
- return l;
+ protected List<T> readList(final String header) throws ParseException {
+ return readList(new ArrayList<T>(), header);
+ }
+
+ private List<T> readList(final List<T> l, final String header)
+ throws ParseException {
+
+// List<T> list = null;
+ List<T> list = LIST_CACHE.getIfPresent(header);
+
+ if (list == null) {
+ synchronized (LIST_CACHE) {
+ list = LIST_CACHE.getIfPresent(header);
+ if (list == null) {
+ HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
+ HttpHeaderListAdapter adapter = new HttpHeaderListAdapter(reader);
+ list = new LinkedList<>();
+
+ while (reader.hasNext()) {
+ list.add(creator.create(adapter));
+ adapter.reset();
+ if (reader.hasNext()) {
+ reader.next();
+ }
+ }
+ LIST_CACHE.put(header, list);
+ }
+ }
+ }
+
+ l.addAll(list);
+ return l;
+ }
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
index 81b9f76..15a00cb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
@@ -50,11 +50,16 @@
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.RuntimeDelegate;
import javax.xml.transform.Source;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.PropertiesDelegate;
import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
+import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.message.MessageBodyWorkers;
/**
@@ -90,11 +95,14 @@
private static final List<AcceptableMediaType> WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST =
Collections.singletonList(MediaTypes.WILDCARD_ACCEPTABLE_TYPE);
- private final MultivaluedMap<String, String> headers;
+ private final GuardianStringKeyMultivaluedMap<String> headers;
private final EntityContent entityContent;
private final boolean translateNce;
private MessageBodyWorkers workers;
private final Configuration configuration;
+ private final RuntimeDelegate runtimeDelegateDecorator;
+ private LazyValue<MediaType> contentTypeCache;
+ private LazyValue<List<AcceptableMediaType>> acceptTypeCache;
/**
* Input stream and its state. State is represented by the {@link Type Type enum} and
@@ -158,10 +166,16 @@
* as required by JAX-RS specification on the server side.
*/
public InboundMessageContext(Configuration configuration, boolean translateNce) {
- this.headers = HeaderUtils.createInbound();
+ this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createInbound());
this.entityContent = new EntityContent();
this.translateNce = translateNce;
this.configuration = configuration;
+ runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
+
+ contentTypeCache = contentTypeCache();
+ acceptTypeCache = acceptTypeCache();
+ headers.setGuard(HttpHeaders.CONTENT_TYPE);
+ headers.setGuard(HttpHeaders.ACCEPT);
}
/**
@@ -196,7 +210,7 @@
* @return updated context.
*/
public InboundMessageContext header(String name, Object value) {
- getHeaders().add(name, HeaderUtils.asString(value, configuration));
+ getHeaders().add(name, HeaderUtils.asString(value, runtimeDelegateDecorator));
return this;
}
@@ -208,7 +222,7 @@
* @return updated context.
*/
public InboundMessageContext headers(String name, Object... values) {
- this.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), configuration));
+ this.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), runtimeDelegateDecorator));
return this;
}
@@ -265,7 +279,7 @@
final LinkedList<String> linkedList = new LinkedList<String>();
for (Object element : values) {
- linkedList.add(HeaderUtils.asString(element, configuration));
+ linkedList.add(HeaderUtils.asString(element, runtimeDelegateDecorator));
}
return linkedList;
@@ -332,7 +346,7 @@
}
try {
- return converter.apply(HeaderUtils.asString(value, configuration));
+ return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
} catch (ProcessingException ex) {
throw exception(name, value, ex);
}
@@ -447,18 +461,26 @@
* message entity).
*/
public MediaType getMediaType() {
- return singleHeader(HttpHeaders.CONTENT_TYPE, new Function<String, MediaType>() {
- @Override
- public MediaType apply(String input) {
- try {
- return RuntimeDelegateDecorator.configured(configuration)
- .createHeaderDelegate(MediaType.class)
- .fromString(input);
- } catch (IllegalArgumentException iae) {
- throw new ProcessingException(iae);
- }
- }
- }, false);
+ if (headers.isObservedAndReset(HttpHeaders.CONTENT_TYPE) && contentTypeCache.isInitialized()) {
+ contentTypeCache = contentTypeCache(); // headers changed -> drop cache
+ }
+ return contentTypeCache.get();
+ }
+
+ private LazyValue<MediaType> contentTypeCache() {
+ return Values.lazy((Value<MediaType>) () -> singleHeader(
+ HttpHeaders.CONTENT_TYPE, new Function<String, MediaType>() {
+ @Override
+ public MediaType apply(String input) {
+ try {
+ return runtimeDelegateDecorator
+ .createHeaderDelegate(MediaType.class)
+ .fromString(input);
+ } catch (IllegalArgumentException iae) {
+ throw new ProcessingException(iae);
+ }
+ }
+ }, false));
}
/**
@@ -468,17 +490,26 @@
* to their q-value, with highest preference first.
*/
public List<AcceptableMediaType> getQualifiedAcceptableMediaTypes() {
- final String value = getHeaderString(HttpHeaders.ACCEPT);
-
- if (value == null || value.isEmpty()) {
- return WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST;
+ if (headers.isObservedAndReset(HttpHeaders.ACCEPT) && acceptTypeCache.isInitialized()) {
+ acceptTypeCache = acceptTypeCache();
}
+ return acceptTypeCache.get();
+ }
- try {
- return Collections.unmodifiableList(HttpHeaderReader.readAcceptMediaType(value));
- } catch (ParseException e) {
- throw exception(HttpHeaders.ACCEPT, value, e);
- }
+ private LazyValue<List<AcceptableMediaType>> acceptTypeCache() {
+ return Values.lazy((Value<List<AcceptableMediaType>>) () -> {
+ final String value = getHeaderString(HttpHeaders.ACCEPT);
+
+ if (value == null || value.isEmpty()) {
+ return WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST;
+ }
+
+ try {
+ return Collections.unmodifiableList(HttpHeaderReader.readAcceptMediaType(value));
+ } catch (ParseException e) {
+ throw exception(HttpHeaders.ACCEPT, value, e);
+ }
+ });
}
/**
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
index 64a89d7..2092ad9 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -47,11 +47,16 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.ext.RuntimeDelegate;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
/**
* Base outbound message context implementation.
@@ -63,9 +68,11 @@
private static final List<MediaType> WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST =
Collections.<MediaType>singletonList(MediaTypes.WILDCARD_ACCEPTABLE_TYPE);
- private final MultivaluedMap<String, Object> headers;
+ private final GuardianStringKeyMultivaluedMap<Object> headers;
private final CommittingOutputStream committingOutputStream;
private Configuration configuration;
+ private RuntimeDelegate runtimeDelegateDecorator;
+ private LazyValue<MediaType> mediaTypeCache;
private Object entity;
private GenericType<?> entityType;
@@ -101,9 +108,13 @@
*/
public OutboundMessageContext(Configuration configuration) {
this.configuration = configuration;
- this.headers = HeaderUtils.createOutbound();
+ this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
this.committingOutputStream = new CommittingOutputStream();
this.entityStream = committingOutputStream;
+ this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
+ this.mediaTypeCache = mediaTypeCache();
+
+ headers.setGuard(HttpHeaders.CONTENT_LENGTH);
}
/**
@@ -113,7 +124,8 @@
* @param original the original outbound message context.
*/
public OutboundMessageContext(OutboundMessageContext original) {
- this.headers = HeaderUtils.createOutbound();
+ this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
+ this.headers.setGuard(HttpHeaders.CONTENT_LENGTH);
this.headers.putAll(original.headers);
this.committingOutputStream = new CommittingOutputStream();
this.entityStream = committingOutputStream;
@@ -122,6 +134,8 @@
this.entityType = original.entityType;
this.entityAnnotations = original.entityAnnotations;
this.configuration = original.configuration;
+ this.runtimeDelegateDecorator = original.runtimeDelegateDecorator;
+ this.mediaTypeCache = original.mediaTypeCache();
}
/**
@@ -153,7 +167,7 @@
* @return multi-valued map of outbound message header names to their string-converted values.
*/
public MultivaluedMap<String, String> getStringHeaders() {
- return HeaderUtils.asStringHeaders(headers, configuration);
+ return HeaderUtils.asStringHeaders(headers, runtimeDelegateDecorator);
}
/**
@@ -173,7 +187,7 @@
* character.
*/
public String getHeaderString(String name) {
- return HeaderUtils.asHeaderString(headers.get(name), RuntimeDelegateDecorator.configured(configuration));
+ return HeaderUtils.asHeaderString(headers.get(name), runtimeDelegateDecorator);
}
/**
@@ -209,7 +223,7 @@
return valueType.cast(value);
} else {
try {
- return converter.apply(HeaderUtils.asString(value, null));
+ return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
} catch (ProcessingException ex) {
throw exception(name, value, ex);
}
@@ -267,8 +281,17 @@
* message entity).
*/
public MediaType getMediaType() {
- return singleHeader(HttpHeaders.CONTENT_TYPE, MediaType.class, RuntimeDelegateDecorator.configured(configuration)
- .createHeaderDelegate(MediaType.class)::fromString, false);
+ if (headers.isObservedAndReset(HttpHeaders.CONTENT_TYPE) && mediaTypeCache.isInitialized()) {
+ mediaTypeCache = mediaTypeCache(); // headers changed -> drop cache
+ }
+ return mediaTypeCache.get();
+ }
+
+ private LazyValue<MediaType> mediaTypeCache() {
+ return Values.lazy((Value<MediaType>) () ->
+ singleHeader(HttpHeaders.CONTENT_TYPE, MediaType.class, RuntimeDelegateDecorator.configured(configuration)
+ .createHeaderDelegate(MediaType.class)::fromString, false)
+ );
}
/**
@@ -294,7 +317,7 @@
result.add(_value);
} else {
conversionApplied = true;
- result.addAll(HttpHeaderReader.readAcceptMediaType(HeaderUtils.asString(value, configuration)));
+ result.addAll(HttpHeaderReader.readAcceptMediaType(HeaderUtils.asString(value, runtimeDelegateDecorator)));
}
} catch (java.text.ParseException e) {
throw exception(HttpHeaders.ACCEPT, value, e);
@@ -333,7 +356,7 @@
} else {
conversionApplied = true;
try {
- result.addAll(HttpHeaderReader.readAcceptLanguage(HeaderUtils.asString(value, configuration))
+ result.addAll(HttpHeaderReader.readAcceptLanguage(HeaderUtils.asString(value, runtimeDelegateDecorator))
.stream()
.map(LanguageTag::getAsLocale)
.collect(Collectors.toList()));
@@ -366,7 +389,7 @@
}
Map<String, Cookie> result = new HashMap<String, Cookie>();
- for (String cookie : HeaderUtils.asStringList(cookies, configuration)) {
+ for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
if (cookie != null) {
result.putAll(HttpHeaderReader.readCookies(cookie));
}
@@ -454,7 +477,7 @@
}
Map<String, NewCookie> result = new HashMap<String, NewCookie>();
- for (String cookie : HeaderUtils.asStringList(cookies, configuration)) {
+ for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
if (cookie != null) {
NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
String cookieName = newCookie.getName();
@@ -542,7 +565,7 @@
} else {
conversionApplied = true;
try {
- result.add(Link.valueOf(HeaderUtils.asString(value, configuration)));
+ result.add(Link.valueOf(HeaderUtils.asString(value, runtimeDelegateDecorator)));
} catch (IllegalArgumentException e) {
throw exception(HttpHeaders.LINK, value, e);
}
@@ -863,6 +886,7 @@
void setConfiguration(Configuration configuration) {
this.configuration = configuration;
+ this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
}
/**
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java b/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java
index 9687a61..1647f13 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java
@@ -28,6 +28,7 @@
import java.util.logging.Logger;
import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.GenericType;
@@ -161,7 +162,7 @@
}
public RequestContextBuilder type(final MediaType contentType) {
- request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, HeaderUtils.asString(contentType, null));
+ request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, HeaderUtils.asString(contentType, (Configuration) null));
return this;
}
@@ -185,7 +186,7 @@
request.getHeaders().remove(name);
return;
}
- request.header(name, HeaderUtils.asString(value, null));
+ request.header(name, HeaderUtils.asString(value, (Configuration) null));
}
private void putHeaders(final String name, final Object... values) {
@@ -193,7 +194,7 @@
request.getHeaders().remove(name);
return;
}
- request.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), null));
+ request.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), (Configuration) null));
}
private void putHeaders(final String name, final String... values) {
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
index 3ac4782..74318b1 100644
--- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
@@ -25,6 +25,7 @@
import java.util.List;
import javax.ws.rs.core.AbstractMultivaluedMap;
+import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.ext.RuntimeDelegate;
@@ -101,19 +102,19 @@
@Test
public void testAsString() throws Exception {
- assertNull(HeaderUtils.asString(null, null));
+ assertNull(HeaderUtils.asString(null, (Configuration) null));
final String value = "value";
- assertSame(value, HeaderUtils.asString(value, null));
+ assertSame(value, HeaderUtils.asString(value, (Configuration) null));
final URI uri = new URI("test");
- assertEquals(uri.toASCIIString(), HeaderUtils.asString(uri, null));
+ assertEquals(uri.toASCIIString(), HeaderUtils.asString(uri, (Configuration) null));
}
@Test
public void testAsStringList() throws Exception {
- assertNotNull(HeaderUtils.asStringList(null, null));
- assertTrue(HeaderUtils.asStringList(null, null).isEmpty());
+ assertNotNull(HeaderUtils.asStringList(null, (Configuration) null));
+ assertTrue(HeaderUtils.asStringList(null, (Configuration) null).isEmpty());
final URI uri = new URI("test");
final List<Object> values = new LinkedList<Object>() {{
@@ -123,7 +124,7 @@
}};
// test string values
- final List<String> stringList = HeaderUtils.asStringList(values, null);
+ final List<String> stringList = HeaderUtils.asStringList(values, (Configuration) null);
assertEquals(Arrays.asList("value", "[null]", uri.toASCIIString()),
stringList);
@@ -138,7 +139,7 @@
@Test
public void testAsStringHeaders() throws Exception {
- assertNull(HeaderUtils.asStringHeaders(null, null));
+ assertNull(HeaderUtils.asStringHeaders(null, (Configuration) null));
final AbstractMultivaluedMap<String, Object> headers = HeaderUtils.createOutbound();
@@ -150,7 +151,7 @@
headers.putSingle("k3", "value3");
- final MultivaluedMap<String, String> stringHeaders = HeaderUtils.asStringHeaders(headers, null);
+ final MultivaluedMap<String, String> stringHeaders = HeaderUtils.asStringHeaders(headers, (Configuration) null);
// test string values
assertEquals(Arrays.asList("value", "value2"),
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
index 9d118a5..2218d91 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
@@ -16,6 +16,7 @@
package org.glassfish.jersey.tests.e2e.header;
+
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.internal.ServiceFinder;
@@ -36,6 +37,7 @@
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
@@ -155,7 +157,7 @@
public void testHeaderDelegateIsUsedWhenRuntimeDelegateDecoratorIsUsed() {
MultivaluedHashMap headers = new MultivaluedHashMap();
headers.put(HEADER_NAME, Arrays.asList(new BeanForHeaderDelegateProviderTest()));
- MultivaluedMap<String, String> converted = HeaderUtils.asStringHeaders(headers, null);
+ MultivaluedMap<String, String> converted = HeaderUtils.asStringHeaders(headers, (Configuration) null);
testMap(converted, BeanForHeaderDelegateProviderTest.getValue());
Client client = ClientBuilder.newClient().property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE, false);
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
index f1f42bc..5730e4b 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
@@ -24,6 +24,7 @@
import java.util.Locale;
import java.util.Set;
+import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
@@ -247,7 +248,7 @@
}
MultivaluedMap<String, String> mvp = HeaderUtils.asStringHeaders(
- resp.getMetadata(), null);
+ resp.getMetadata(), (Configuration) null);
for (String key : mvp.keySet()) {
sb.append(indent + "Processing Key found in response: ").append(key).append(": ").append(mvp.get(key)).append("; ")
diff --git a/tests/performance/benchmarks/pom.xml b/tests/performance/benchmarks/pom.xml
index b977edc..30ce813 100644
--- a/tests/performance/benchmarks/pom.xml
+++ b/tests/performance/benchmarks/pom.xml
@@ -79,6 +79,10 @@
<type>pom</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jdk-http</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
index badc3ee..3f729d0 100644
--- a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -36,6 +36,7 @@
.include(JacksonBenchmark.class.getSimpleName())
.include(LocatorBenchmark.class.getSimpleName())
.include(JerseyUriBuilderBenchmark.class.getSimpleName())
+ .include(HeadersServerBenchmark.class.getName())
// Measure throughput in seconds (ops/s).
.mode(Mode.Throughput)
.timeUnit(TimeUnit.SECONDS)
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersClientBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersClientBenchmark.java
new file mode 100644
index 0000000..fcd137a
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersClientBenchmark.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark;
+
+import com.sun.net.httpserver.HttpServer;
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersMBRW;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersResource;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersApplication;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@Threads(4)
+@State(Scope.Benchmark)
+public class HeadersClientBenchmark {
+
+ static final String BASE_URI = "http://localhost:9009/headers";
+
+ private static final AtomicInteger counter = new AtomicInteger();
+ private static final MediaType MEDIA_PLAIN = MediaType.valueOf(HeadersResource.MEDIA_PLAIN);
+ private static final MediaType MEDIA_JSON = MediaType.valueOf(HeadersResource.MEDIA_JSON);
+
+ private static final boolean INCLUDE_INIT = false;
+
+ private volatile WebTarget webTarget;
+
+ @Setup
+ public void setUp() {
+ if (!INCLUDE_INIT) {
+ webTarget = ClientBuilder.newClient(config()).target(BASE_URI);
+ }
+ }
+
+ private WebTarget webTarget() {
+ return INCLUDE_INIT ? ClientBuilder.newClient(config()).target(BASE_URI) : webTarget;
+ }
+
+ private static class JdkServer {
+ private HttpServer server;
+ void start() {
+ server = JdkHttpServerFactory.createHttpServer(URI.create(BASE_URI), new HeadersApplication(), null, false);
+ server.start();
+ try {
+ TimeUnit.SECONDS.sleep(1L);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ void stop() {
+ server.stop(1);
+ }
+ }
+
+ private static class GrizzlyServer {
+ private org.glassfish.grizzly.http.server.HttpServer httpServer;
+ void start() {
+ httpServer = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), new HeadersApplication(), null, false);
+ try {
+ httpServer.start();
+ TimeUnit.SECONDS.sleep(1L);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ void stop() {
+ httpServer.shutdownNow();
+ }
+ }
+
+ @Benchmark
+ public void testGetPlainTextClient() {
+ WebTarget target = webTarget().path("headers/getPlain");
+ try (Response r = target.request(MEDIA_PLAIN).get()) {
+ consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+ }
+ }
+
+ @Benchmark
+ public void testGetJsonClient() {
+ WebTarget target = webTarget().path("headers/getJson");
+ try (Response r = target.request(MEDIA_JSON).get()) {
+ consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+ }
+ }
+
+ @Benchmark
+ public void testPostPlainTextClient() {
+ WebTarget target = webTarget().path("headers/postPlain");
+ try (Response r = target.request(MEDIA_PLAIN).post(Entity.entity(HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN))) {
+ consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+ }
+ }
+
+ @Benchmark
+ public void testPostJsonClient() {
+ WebTarget target = webTarget().path("headers/postJson");
+ try (Response r = target.request(MEDIA_JSON).post(Entity.entity(HeadersResource.CONTENT_PLAIN, MEDIA_JSON))) {
+ consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+ }
+ }
+
+ @Benchmark
+ public void testRandomClient() {
+ switch (counter.incrementAndGet() % 4) {
+ case 0:
+ testGetJsonClient();
+ break;
+ case 1:
+ testGetPlainTextClient();
+ break;
+ case 2:
+ testPostJsonClient();
+ break;
+ case 3:
+ testPostPlainTextClient();
+ break;
+ }
+ }
+
+ private ClientConfig config() {
+ ClientConfig config = new ClientConfig();
+ config.property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL");
+ config.register(HeadersMBRW.class);
+ return config;
+ }
+
+ private void consume(Response response, String expectedContent, MediaType expectedMedia) {
+ if (response.getStatus() != 200) {
+ throw new IllegalStateException("Status:" + response.getStatus());
+ }
+ String content = response.readEntity(String.class);
+ if (!expectedContent.equals(content)) {
+ throw new IllegalStateException("Content:" + content);
+ }
+ if (!expectedMedia.equals(response.getMediaType())) {
+ throw new IllegalStateException("ContentType:" + response.getMediaType());
+ }
+ }
+
+ public static void main(String[] args) throws RunnerException {
+// JdkServer server = new JdkServer();
+ GrizzlyServer server = new GrizzlyServer();
+ server.start();
+
+ final Options opt = new OptionsBuilder()
+ // Register our benchmarks.
+ .include(HeadersClientBenchmark.class.getSimpleName())
+// .addProfiler(org.openjdk.jmh.profile.JavaFlightRecorderProfiler.class)
+ .build();
+
+ try {
+ new Runner(opt).run();
+ //new HeadersBenchmark().testGetJsonClient();
+ } finally {
+ server.stop();
+ }
+
+ }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersServerBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersServerBenchmark.java
new file mode 100644
index 0000000..2494a15
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersServerBenchmark.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.test.util.server.ContainerRequestBuilder;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersApplication;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersResource;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import javax.ws.rs.core.MediaType;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@Threads(4)
+@State(Scope.Benchmark)
+public class HeadersServerBenchmark {
+ private static final AtomicInteger counter = new AtomicInteger();
+ private static final MediaType MEDIA_PLAIN = MediaType.valueOf(HeadersResource.MEDIA_PLAIN);
+ private static final MediaType MEDIA_JSON = MediaType.valueOf(HeadersResource.MEDIA_JSON);
+
+ private volatile ApplicationHandler handler;
+
+ @Setup
+ public void start() throws Exception {
+ handler = new ApplicationHandler(new HeadersApplication());
+ }
+
+ @TearDown
+ public void shutdown() {
+ if (counter.get() != 0) {
+ System.out.append("Executed ").append(String.valueOf(counter.get())).append(" requests");
+ }
+ }
+
+ @Benchmark
+ public void testGetPlainText() throws ExecutionException, InterruptedException {
+ ContainerRequest request = ContainerRequestBuilder
+ .from("headers/getPlain", "GET", handler.getConfiguration())
+ .accept(MEDIA_PLAIN)
+ .build();
+
+ ContainerResponse response = handler.apply(request).get();
+ consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+ }
+
+ @Benchmark
+ public void testGetJson() throws ExecutionException, InterruptedException {
+ ContainerRequest request = ContainerRequestBuilder
+ .from("headers/getJson", "GET", handler.getConfiguration())
+ .accept(MEDIA_JSON)
+ .build();
+
+ ContainerResponse response = handler.apply(request).get();
+ consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+ }
+
+ @Benchmark
+ public void testPostPlainText() throws ExecutionException, InterruptedException {
+ ContainerRequest request = ContainerRequestBuilder
+ .from("headers/postPlain", "POST", handler.getConfiguration())
+ .accept(MEDIA_PLAIN)
+ .type(MEDIA_PLAIN)
+ .entity(HeadersResource.CONTENT_PLAIN, handler)
+ .build();
+
+ ContainerResponse response = handler.apply(request).get();
+ consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+ }
+
+ @Benchmark
+ public void testPostJson() throws ExecutionException, InterruptedException {
+ ContainerRequest request = ContainerRequestBuilder
+ .from("headers/postJson", "POST", handler.getConfiguration())
+ .accept(MEDIA_JSON)
+ .type(MEDIA_JSON)
+ .entity(HeadersResource.CONTENT_PLAIN, handler)
+ .build();
+
+ ContainerResponse response = handler.apply(request).get();
+ consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+ }
+
+ @Benchmark
+ public void testRandomClient() throws ExecutionException, InterruptedException {
+ switch (counter.incrementAndGet() % 4) {
+ case 0:
+ testGetJson();
+ break;
+ case 1:
+ testGetPlainText();
+ break;
+ case 2:
+ testPostJson();
+ break;
+ case 3:
+ testPostPlainText();
+ break;
+ }
+ }
+
+ private void consume(ContainerResponse response, String expectedContent, MediaType expectedMedia) {
+ if (response.getStatus() != 200) {
+ throw new IllegalStateException("Status:" + response.getStatus());
+ }
+ String content = response.getEntity().toString();
+ if (!expectedContent.equals(content)) {
+ throw new IllegalStateException("Content:" + content);
+ }
+ if (!expectedMedia.equals(response.getMediaType())) {
+ throw new IllegalStateException("ContentType:" + response.getMediaType());
+ }
+ }
+
+ public static void main(String[] args) throws RunnerException {
+ final Options opt = new OptionsBuilder()
+ // Register our benchmarks.
+ .include(HeadersServerBenchmark.class.getSimpleName())
+// .addProfiler(org.openjdk.jmh.profile.JavaFlightRecorderProfiler.class)
+ .build();
+
+ new Runner(opt).run();
+
+// DEBUG:
+
+// try {
+// HeadersServerBenchmark benchmark = new HeadersServerBenchmark();
+// benchmark.start();
+// for (int i = 0; i != 5; i++) {
+// benchmark.testPostPlainText();
+// }
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+ }
+
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersApplication.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersApplication.java
new file mode 100644
index 0000000..6ec127d
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark.headers;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+
+public class HeadersApplication extends ResourceConfig {
+ public HeadersApplication() {
+ register(HeadersResource.class).register(HeadersMBRW.class);
+
+ // Turn off Monitoring to not affect benchmarks.
+ property(ServerProperties.MONITORING_ENABLED, false);
+ property(ServerProperties.MONITORING_STATISTICS_ENABLED, false);
+ property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, false);
+
+ property(ServerProperties.WADL_FEATURE_DISABLE, true);
+ property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL");
+ }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersMBRW.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersMBRW.java
new file mode 100644
index 0000000..ea34696
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersMBRW.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark.headers;
+
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+public class HeadersMBRW implements MessageBodyReader<String>, MessageBodyWriter<String> {
+
+ private static final JacksonJsonProvider jackson = new JacksonJsonProvider();
+
+ @Override
+ public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return (type == String.class && HeadersResource.MEDIA_PLAIN.equals(mediaType.toString()))
+ || (type == String.class && HeadersResource.MEDIA_JSON.equals(mediaType.toString()));
+ }
+
+ @Override
+ public String readFrom(Class<String> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+ throws IOException, WebApplicationException {
+ switch (mediaType.toString()) {
+ case HeadersResource.MEDIA_PLAIN:
+ return ReaderWriter.readFromAsString(entityStream, MediaType.TEXT_PLAIN_TYPE);
+ case HeadersResource.MEDIA_JSON:
+ return jackson.readFrom((Class<Object>) (Class) type, genericType, annotations, mediaType,
+ httpHeaders, entityStream).toString();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return isReadable(type, genericType, annotations, mediaType);
+ }
+
+ @Override
+ public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+ throws IOException, WebApplicationException {
+ switch (mediaType.toString()) {
+ case HeadersResource.MEDIA_PLAIN:
+ ReaderWriter.writeToAsString(s, entityStream, MediaType.TEXT_PLAIN_TYPE);
+ break;
+ case HeadersResource.MEDIA_JSON:
+ jackson.writeTo(s, type, genericType, annotations, MediaType.APPLICATION_JSON_TYPE, httpHeaders, entityStream);
+ break;
+ }
+ }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersResource.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersResource.java
new file mode 100644
index 0000000..86558f0
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersResource.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark.headers;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+@Path("/headers")
+public class HeadersResource {
+ public static final String MEDIA_PLAIN = "myapplication/someplaintext+someothertoolongtype";
+ public static final String MEDIA_JSON = "myapplication/somejson+someradiculouslylongtype";
+
+ public static final String CONTENT_PLAIN = "some plain text content does not matter";
+ public static final String CONTENT_JSON = "\"" + CONTENT_PLAIN + "\"";
+
+ @GET
+ @Produces(MEDIA_PLAIN)
+ @Path("getPlain")
+ public String getMediaPlain() {
+ return CONTENT_PLAIN;
+ }
+
+ @POST
+ @Produces(MEDIA_PLAIN)
+ @Consumes(MEDIA_PLAIN)
+ @Path("postPlain")
+ public String postMediaPlain(String content) {
+ if (!CONTENT_PLAIN.equals(content)) {
+ throw new WebApplicationException(Response.Status.EXPECTATION_FAILED);
+ }
+ return CONTENT_PLAIN;
+ }
+
+ @GET
+ @Produces(MEDIA_JSON)
+ @Path("getJson")
+ public Response getJson() {
+ return Response.ok(CONTENT_PLAIN, MEDIA_JSON).build();
+ }
+
+ @POST
+ @Produces(MEDIA_JSON)
+ @Consumes(MEDIA_JSON)
+ @Path("postJson")
+ public Response postJson(String json) {
+ if (!CONTENT_PLAIN.equals(json)) {
+ throw new WebApplicationException(Response.Status.EXPECTATION_FAILED);
+ }
+ return Response.ok(CONTENT_PLAIN, MEDIA_JSON).build();
+ }
+
+}