Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guard list of headers for modifications #5320

Merged
merged 1 commit into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@

package org.glassfish.jersey.internal.util.collection;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

/**
* The {@link MultivaluedMap} wrapper that is able to set guards observing changes of values represented by a key.
Expand All @@ -35,6 +39,10 @@ public class GuardianStringKeyMultivaluedMap<V> implements MultivaluedMap<String
private final MultivaluedMap<String, V> inner;
private final Map<String, Boolean> guards = new HashMap<>();

private static boolean isMutable(Object mutable) {
return !String.class.isInstance(mutable) && !MediaType.class.isInstance(mutable);
}

public GuardianStringKeyMultivaluedMap(MultivaluedMap<String, V> inner) {
this.inner = inner;
}
Expand All @@ -53,7 +61,11 @@ public void add(String key, V value) {

@Override
public V getFirst(String key) {
return inner.getFirst(key);
V first = inner.getFirst(key);
if (isMutable(key)) {
observe(key);
}
return first;
}

@Override
Expand Down Expand Up @@ -101,7 +113,15 @@ public boolean containsValue(Object value) {

@Override
public List<V> get(Object key) {
return inner.get(key);
final List<V> innerList = inner.get(key);
if (innerList != null) {
for (Map.Entry<String, Boolean> guard : guards.entrySet()) {
if (guard.getKey().equals(key)) {
return new GuardianList(innerList, guard);
}
}
}
return innerList;
}

@Override
Expand Down Expand Up @@ -139,11 +159,13 @@ public Set<String> keySet() {

@Override
public Collection<List<V>> values() {
observeAll();
return inner.values();
}

@Override
public Set<Entry<String, List<V>>> entrySet() {
observeAll();
return inner.entrySet();
}

Expand Down Expand Up @@ -208,4 +230,225 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(inner, guards);
}

private static class MutableGuardian<V> {
protected final Map.Entry<String, Boolean> guard;

private MutableGuardian(Entry<String, Boolean> guard) {
this.guard = guard;
}

protected V guardMutable(V mutable) {
if (isMutable(mutable)) {
guard.setValue(true);
}
return mutable;
}
}

private static class GuardianList<V> extends MutableGuardian<V> implements List<V> {
private final List<V> guarded;

public GuardianList(List<V> guarded, Map.Entry<String, Boolean> guard) {
super(guard);
this.guarded = guarded;
}

@Override
public int size() {
return guarded.size();
}

@Override
public boolean isEmpty() {
return guarded.isEmpty();
}

@Override
public boolean contains(Object o) {
return guarded.contains(o);
}

@Override
public Iterator<V> iterator() {
return new GuardianIterator<>(guarded.iterator(), guard);
}

@Override
public Object[] toArray() {
guard.setValue(true);
return guarded.toArray();
}

@Override
public <T> T[] toArray(T[] a) {
guard.setValue(true);
return guarded.toArray(a);
}

@Override
public boolean add(V e) {
guard.setValue(true);
return guarded.add(e);
}

@Override
public boolean remove(Object o) {
guard.setValue(true);
return guarded.remove(o);
}

@Override
public boolean containsAll(Collection<?> c) {
return guarded.containsAll(c);
}

@Override
public boolean addAll(Collection<? extends V> c) {
guard.setValue(true);
return guarded.addAll(c);
}

@Override
public boolean addAll(int index, Collection<? extends V> c) {
guard.setValue(true);
return guarded.addAll(index, c);
}

@Override
public boolean removeAll(Collection<?> c) {
guard.setValue(true);
return guarded.removeAll(c);
}

@Override
public boolean retainAll(Collection<?> c) {
guard.setValue(true);
return guarded.retainAll(c);
}

@Override
public void clear() {
guard.setValue(true);
guarded.clear();
}

@Override
public V get(int index) {
return guardMutable(guarded.get(index));
}

@Override
public V set(int index, V element) {
guard.setValue(true);
return guarded.set(index, element);
}

@Override
public void add(int index, V element) {
guard.setValue(true);
guarded.add(index, element);
}

@Override
public V remove(int index) {
guard.setValue(true);
return guarded.remove(index);
}

@Override
public int indexOf(Object o) {
return guarded.indexOf(o);
}

@Override
public int lastIndexOf(Object o) {
return guarded.lastIndexOf(o);
}

@Override
public ListIterator<V> listIterator() {
return new GuardianListIterator<>(guarded.listIterator(), guard);
}

@Override
public ListIterator<V> listIterator(int index) {
return new GuardianListIterator<>(guarded.listIterator(index), guard);
}

@Override
public List<V> subList(int fromIndex, int toIndex) {
final List<V> sublist = guarded.subList(fromIndex, toIndex);
return sublist != null ? new GuardianList<>(sublist, guard) : sublist;
}
}

private static class GuardianIterator<V> extends MutableGuardian<V> implements Iterator<V> {
protected final Iterator<V> guarded;

public GuardianIterator(Iterator<V> guarded, Map.Entry<String, Boolean> guard) {
super(guard);
this.guarded = guarded;
}

@Override
public boolean hasNext() {
return guarded.hasNext();
}

@Override
public V next() {
return guardMutable(guarded.next());
}

@Override
public void remove() {
guard.setValue(true);
guarded.remove();
}

@Override
public void forEachRemaining(Consumer<? super V> action) {
guarded.forEachRemaining(action);
}
}

private static class GuardianListIterator<V> extends GuardianIterator<V> implements ListIterator<V> {

public GuardianListIterator(Iterator<V> guarded, Entry<String, Boolean> guard) {
super(guarded, guard);
}

@Override
public boolean hasPrevious() {
return ((ListIterator<V>) guarded).hasPrevious();
}

@Override
public V previous() {
return guardMutable(((ListIterator<V>) guarded).previous());
}

@Override
public int nextIndex() {
return ((ListIterator<V>) guarded).nextIndex();
}

@Override
public int previousIndex() {
return ((ListIterator<V>) guarded).previousIndex();
}

@Override
public void set(V v) {
((ListIterator<V>) guarded).set(v);
guard.setValue(true);
}

@Override
public void add(V v) {
((ListIterator<V>) guarded).add(v);
guard.setValue(true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.net.URISyntaxException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -236,6 +237,33 @@ public void testChangedContentType() {
Assertions.assertEquals(MediaType.APPLICATION_JSON_TYPE, ctx.getMediaType());
}

@Test
public void testChangedContentTypeOnList() {
OutboundMessageContext ctx = new OutboundMessageContext((Configuration) null);
ctx.setMediaType(MediaType.APPLICATION_XML_TYPE);
Assertions.assertEquals(MediaType.APPLICATION_XML_TYPE, ctx.getMediaType());
ctx.getHeaders().get(HttpHeaders.CONTENT_TYPE).set(0, MediaType.APPLICATION_JSON);
Assertions.assertEquals(MediaType.APPLICATION_JSON_TYPE, ctx.getMediaType());
}

@Test
public void testChangedContentTypeOnValues() {
OutboundMessageContext ctx = new OutboundMessageContext((Configuration) null);
ctx.setMediaType(MediaType.APPLICATION_XML_TYPE);
Assertions.assertEquals(MediaType.APPLICATION_XML_TYPE, ctx.getMediaType());
ctx.getHeaders().values().clear();
Assertions.assertEquals(null, ctx.getMediaType());
}

@Test
public void testChangedContentTypeOnEntrySet() {
OutboundMessageContext ctx = new OutboundMessageContext((Configuration) null);
ctx.setMediaType(MediaType.APPLICATION_XML_TYPE);
Assertions.assertEquals(MediaType.APPLICATION_XML_TYPE, ctx.getMediaType());
ctx.getHeaders().entrySet().clear();
Assertions.assertEquals(null, ctx.getMediaType());
}

@Test
public void testCopyConstructor() {
OutboundMessageContext ctx = new OutboundMessageContext((Configuration) null);
Expand Down