You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2022/08/12 22:14:32 UTC

[juneau] branch jbFixRestNpe updated: HeaderList should extend from ArrayList.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch jbFixRestNpe
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/jbFixRestNpe by this push:
     new 0a701d574 HeaderList should extend from ArrayList.
0a701d574 is described below

commit 0a701d574313d15507f983fce6f948505e748169
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Fri Aug 12 18:14:02 2022 -0400

    HeaderList should extend from ArrayList.
---
 .../org/apache/juneau/rest/client/RestRequest.java |   4 +-
 .../apache/juneau/rest/client/RestResponse.java    |   4 +-
 .../org/apache/juneau/http/header/HeaderList.java  | 265 ++++++---------------
 .../org/apache/juneau/http/part/PartIterator.java  |  11 +-
 .../java/org/apache/juneau/http/part/PartList.java |  27 +--
 .../juneau/http/response/BasicHttpException.java   |   4 +-
 .../juneau/http/response/BasicHttpResponse.java    |   4 +-
 .../apache/juneau/http/header/HeaderList_Test.java |  24 +-
 .../org/apache/juneau/http/part/PartList_Test.java |  26 +-
 .../org/apache/juneau/http/remote/Remote_Test.java |   3 +
 10 files changed, 114 insertions(+), 258 deletions(-)

diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
index 2d2e9e251..7aa2328e5 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
@@ -2486,7 +2486,7 @@ public class RestRequest extends BeanSession implements HttpUriRequest, Configur
 	 */
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator() {
-		return getHeaderList().iterator();
+		return getHeaderList().headerIterator();
 	}
 
 	/**
@@ -2497,7 +2497,7 @@ public class RestRequest extends BeanSession implements HttpUriRequest, Configur
 	 */
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator(String name) {
-		return getHeaderList().iterator(name);
+		return getHeaderList().headerIterator(name);
 	}
 
 	/**
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestResponse.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestResponse.java
index ee7e038ed..6ed543d01 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestResponse.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestResponse.java
@@ -797,7 +797,7 @@ public class RestResponse implements HttpResponse {
 	 */
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator() {
-		return headers.iterator();
+		return headers.headerIterator();
 	}
 
 	/**
@@ -808,7 +808,7 @@ public class RestResponse implements HttpResponse {
 	 */
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator(String name) {
-		return headers.iterator(name);
+		return headers.headerIterator(name);
 	}
 
 	/**
diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/HeaderList.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/HeaderList.java
index b7d2eb098..c26f008b4 100644
--- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/HeaderList.java
+++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/HeaderList.java
@@ -24,6 +24,7 @@ import java.util.stream.*;
 import org.apache.http.*;
 import org.apache.http.util.*;
 import org.apache.juneau.*;
+import org.apache.juneau.collections.*;
 import org.apache.juneau.cp.*;
 import org.apache.juneau.http.HttpHeaders;
 import org.apache.juneau.internal.*;
@@ -90,7 +91,7 @@ import org.apache.juneau.svl.*;
  * Various methods are provided for iterating over the headers in this list to avoid array copies.
  * <ul class='javatree'>
  * 	<li class='jm'>{@link #forEach(Consumer)} / {@link #forEach(String,Consumer)} / {@link #forEach(Predicate,Consumer)} - Use consumers to process headers.
- * 	<li class='jm'>{@link #iterator()} / {@link #iterator(String)} - Use an {@link HeaderIterator} to process headers.
+ * 	<li class='jm'>{@link #headerIterator()} / {@link #headerIterator(String)} - Use an {@link HeaderIterator} to process headers.
  * 	<li class='jm'>{@link #stream()} / {@link #stream(String)} - Use a stream.
  * </ul>
  * <p>
@@ -168,21 +169,27 @@ import org.apache.juneau.svl.*;
  * 	<li class='extlink'>{@source}
  * </ul>
  */
-public class HeaderList {
+public class HeaderList extends ControlledArrayList<Header>{
 
 	//-----------------------------------------------------------------------------------------------------------------
 	// Static
 	//-----------------------------------------------------------------------------------------------------------------
 
+	private static final long serialVersionUID = 1L;
 	private static final Header[] EMPTY_ARRAY = new Header[0];
 	private static final String[] EMPTY_STRING_ARRAY = new String[0];
 	private static final Predicate<Header> NOT_NULL = x -> x != null;
 
 	/** Represents no header supplier in annotations. */
-	public static final class Void extends HeaderList {}
+	public static final class Void extends HeaderList {
+		Void() {
+			super(false);
+		}
+		private static final long serialVersionUID = 1L;
+	}
 
 	/** Predefined instance. */
-	public static final HeaderList EMPTY = new HeaderList();
+	public static final HeaderList EMPTY = new HeaderList(false);
 
 	/**
 	 * Instantiates a new builder for this bean.
@@ -203,7 +210,7 @@ public class HeaderList {
 	 * @return A new unmodifiable instance, never <jk>null</jk>.
 	 */
 	public static HeaderList of(List<Header> headers) {
-		return headers == null || headers.isEmpty() ? EMPTY : new HeaderList(headers);
+		return headers == null || headers.isEmpty() ? EMPTY : new HeaderList(true, headers);
 	}
 
 	/**
@@ -215,7 +222,7 @@ public class HeaderList {
 	 * @return A new unmodifiable instance, never <jk>null</jk>.
 	 */
 	public static HeaderList of(Header...headers) {
-		return headers == null || headers.length == 0 ? EMPTY : new HeaderList(headers);
+		return headers == null || headers.length == 0 ? EMPTY : new HeaderList(true, headers);
 	}
 
 	/**
@@ -240,7 +247,7 @@ public class HeaderList {
 		ArrayBuilder<Header> b = ArrayBuilder.of(Header.class).filter(NOT_NULL).size(pairs.length / 2);
 		for (int i = 0; i < pairs.length; i+=2)
 			b.add(BasicHeader.of(pairs[i], pairs[i+1]));
-		return new HeaderList(b.orElse(EMPTY_ARRAY));
+		return new HeaderList(true, b.orElse(EMPTY_ARRAY));
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
@@ -256,7 +263,7 @@ public class HeaderList {
 		final List<Header> entries;
 		List<Header> defaultEntries;
 		private VarResolver varResolver;
-		boolean caseSensitive;
+		boolean caseSensitive = false, unmodifiable = false;
 
 		/**
 		 * Constructor.
@@ -273,8 +280,9 @@ public class HeaderList {
 		 */
 		protected Builder(HeaderList copyFrom) {
 			super(copyFrom.getClass(), BeanStore.INSTANCE);
-			entries = list(copyFrom.entries);
+			entries = copyOf(copyFrom);
 			caseSensitive = copyFrom.caseSensitive;
+			unmodifiable = false;
 		}
 
 		/**
@@ -288,6 +296,7 @@ public class HeaderList {
 			defaultEntries = copyOf(copyFrom.defaultEntries);
 			varResolver = copyFrom.varResolver;
 			caseSensitive = copyFrom.caseSensitive;
+			unmodifiable = copyFrom.unmodifiable;
 		}
 
 		@Override /* BeanBuilder */
@@ -371,27 +380,26 @@ public class HeaderList {
 		}
 
 		/**
-		 * Removes any headers already in this builder.
+		 * Specifies that the resulting list should be unmodifiable.
+		 *
+		 * <p>
+		 * The default behavior is modifiable.
 		 *
 		 * @return This object.
 		 */
-		@FluentSetter
-		public Builder clear() {
-			entries.clear();
+		public Builder unmodifiable() {
+			unmodifiable = true;
 			return this;
 		}
 
 		/**
-		 * Adds the specified headers to the end of the headers in this builder.
+		 * Removes any headers already in this builder.
 		 *
-		 * @param value The header to add.  <jk>null</jk> values are ignored.
 		 * @return This object.
 		 */
 		@FluentSetter
-		public Builder append(HeaderList value) {
-			if (value != null)
-				for (Header x : value.entries)
-					append(x);
+		public Builder clear() {
+			entries.clear();
 			return this;
 		}
 
@@ -467,19 +475,6 @@ public class HeaderList {
 			return this;
 		}
 
-		/**
-		 * Adds the specified header to the beginning of the headers in this builder.
-		 *
-		 * @param value The header to add.  <jk>null</jk> values are ignored.
-		 * @return This object.
-		 */
-		@FluentSetter
-		public Builder prepend(HeaderList value) {
-			if (value != null)
-				prependAll(entries, value.entries);
-			return this;
-		}
-
 		/**
 		 * Adds the specified header to the beginning of the headers in this builder.
 		 *
@@ -550,20 +545,6 @@ public class HeaderList {
 			return this;
 		}
 
-		/**
-		 * Removes the specified header from this builder.
-		 *
-		 * @param value The header to remove.  <jk>null</jk> values are ignored.
-		 * @return This object.
-		 */
-		@FluentSetter
-		public Builder remove(HeaderList value) {
-			if (value != null)
-				for (int i = 0; i < value.entries.length; i++)
-					remove(value.entries[i]);
-			return this;
-		}
-
 		/**
 		 * Removes the specified header from this builder.
 		 *
@@ -598,8 +579,8 @@ public class HeaderList {
 		 */
 		@FluentSetter
 		public Builder remove(List<Header> values) {
-			for (int i = 0, j = values.size(); i < j; i++) /* See HTTPCORE-361 */
-				remove(values.get(i));
+			if (values != null)
+				values.forEach(x -> remove(x));
 			return this;
 		}
 
@@ -739,21 +720,6 @@ public class HeaderList {
 			return this;
 		}
 
-		/**
-		 * Replaces the first occurrence of the headers with the same name.
-		 *
-		 * <p>
-		 * If no header with the same name is found the given header is added to the end of the list.
-		 *
-		 * @param values The headers to replace.  <jk>null</jk> values are ignored.
-		 * @return This object.
-		 */
-		public Builder set(HeaderList values) {
-			if (values != null)
-				set(values.entries);
-			return this;
-		}
-
 		/**
 		 * Sets a default value for a header.
 		 *
@@ -863,21 +829,6 @@ public class HeaderList {
 			return this;
 		}
 
-		/**
-		 * Replaces the first occurrence of the headers with the same name.
-		 *
-		 * <p>
-		 * If no header with the same name is found the given header is added to the end of the list.
-		 *
-		 * @param values The default headers to set.  <jk>null</jk> values are ignored.
-		 * @return This object.
-		 */
-		public Builder setDefault(HeaderList values) {
-			if (values != null)
-				setDefault(values.entries);
-			return this;
-		}
-
 		/**
 		 * Adds the specify header to this list.
 		 *
@@ -1018,33 +969,6 @@ public class HeaderList {
 			throw new BasicRuntimeException("Invalid value specified for flag parameter on add(flag,values) method: {0}", flag);
 		}
 
-		/**
-		 * Adds the specified headers to this list.
-		 *
-		 * @param flag
-		 * 	What to do with the header.
-		 * 	<br>Possible values:
-		 * 	<ul>
-		 * 		<li>{@link ListOperation#APPEND APPEND} - Calls {@link #append(HeaderList)}.
-		 * 		<li>{@link ListOperation#PREPEND PREEND} - Calls {@link #prepend(HeaderList)}.
-		 * 		<li>{@link ListOperation#SET REPLACE} - Calls {@link #set(HeaderList)}.
-		 * 		<li>{@link ListOperation#DEFAULT DEFAULT} - Calls {@link #setDefault(HeaderList)}.
-		 * 	</ul>
-		 * @param values The headers to add.
-		 * @return This object.
-		 */
-		public Builder add(ListOperation flag, HeaderList values) {
-			if (flag == ListOperation.APPEND)
-				return append(values);
-			if (flag == ListOperation.PREPEND)
-				return prepend(values);
-			if (flag == ListOperation.SET)
-				return set(values);
-			if (flag == ListOperation.DEFAULT)
-				return setDefault(values);
-			throw new BasicRuntimeException("Invalid value specified for flag parameter on add(flag,values) method: {0}", flag);
-		}
-
 		/**
 		 * Performs an action on all headers in this list.
 		 *
@@ -1102,7 +1026,7 @@ public class HeaderList {
 		 * @param value The header value.
 		 * @return A new header.
 		 */
-		public Header createHeader(String name, Object value) {
+		protected Header createHeader(String name, Object value) {
 			if (value instanceof Supplier<?>) {
 				Supplier<?> value2 = (Supplier<?>)value;
 				return isResolving() ? new BasicHeader(name, resolver(value2)) : new BasicHeader(name, value2);
@@ -1194,7 +1118,6 @@ public class HeaderList {
 	// Instance
 	//-----------------------------------------------------------------------------------------------------------------
 
-	final Header[] entries;
 	final boolean caseSensitive;
 
 	/**
@@ -1203,24 +1126,17 @@ public class HeaderList {
 	 * @param builder The builder containing the settings for this bean.
 	 */
 	public HeaderList(Builder builder) {
-		if (builder.defaultEntries == null) {
-			entries = builder.entries.toArray(new Header[builder.entries.size()]);
-		} else {
-			ArrayBuilder<Header> l = ArrayBuilder.of(Header.class).filter(NOT_NULL).size(builder.entries.size() + builder.defaultEntries.size());
-
-			for (int i = 0, j = builder.entries.size(); i < j; i++)
-				l.add(builder.entries.get(i));
+		super(! builder.unmodifiable, builder.entries);
 
+		if (builder.defaultEntries != null) {
 			for (int i1 = 0, j1 = builder.defaultEntries.size(); i1 < j1; i1++) {
 				Header x = builder.defaultEntries.get(i1);
 				boolean exists = false;
 				for (int i2 = 0, j2 = builder.entries.size(); i2 < j2 && ! exists; i2++)
 					exists = eq(builder.entries.get(i2).getName(), x.getName());
 				if (! exists)
-					l.add(x);
+					overrideAdd(x);
 			}
-
-			entries = l.orElse(EMPTY_ARRAY);
 		}
 		this.caseSensitive = builder.caseSensitive;
 	}
@@ -1228,39 +1144,37 @@ public class HeaderList {
 	/**
 	 * Constructor.
 	 *
+	 * @param modifiable Whether this list should be modifiable.
 	 * @param headers
 	 * 	The headers to add to the list.
 	 * 	<br>Can be <jk>null</jk>.
 	 * 	<br><jk>null</jk> entries are ignored.
 	 */
-	protected HeaderList(List<Header> headers) {
-		ArrayBuilder<Header> l = ArrayBuilder.of(Header.class).filter(NOT_NULL).size(headers.size());
-		for (int i = 0, j = headers.size(); i < j; i++)
-			l.add(headers.get(i));
-		entries = l.orElse(EMPTY_ARRAY);
+	protected HeaderList(boolean modifiable, List<Header> headers) {
+		super(modifiable, headers);
 		caseSensitive = false;
 	}
 
 	/**
 	 * Constructor.
 	 *
+	 * @param modifiable Whether this list should be modifiable.
 	 * @param headers
 	 * 	The headers to add to the list.
 	 * 	<br><jk>null</jk> entries are ignored.
 	 */
-	protected HeaderList(Header...headers) {
-		ArrayBuilder<Header> l = ArrayBuilder.of(Header.class).filter(NOT_NULL).size(headers.length);
-		for (int i = 0; i < headers.length; i++)
-			l.add(headers[i]);
-		entries = l.orElse(EMPTY_ARRAY);
+	protected HeaderList(boolean modifiable, Header...headers) {
+		super(modifiable, Arrays.asList(headers));
 		caseSensitive = false;
 	}
 
 	/**
 	 * Default constructor.
+	 *
+	 * @param modifiable Whether this list should be modifiable.
 	 */
-	protected HeaderList() {
-		entries = EMPTY_ARRAY;
+	protected HeaderList(boolean modifiable) {
+		super(modifiable);
 		caseSensitive = false;
 	}
 
@@ -1287,8 +1201,7 @@ public class HeaderList {
 
 		Header first = null;
 		List<Header> rest = null;
-		for (int i = 0; i < entries.length; i++) {
-			Header x = entries[i];
+		for (Header x : this) {
 			if (eq(x.getName(), name)) {
 				if (first == null)
 					first = x;
@@ -1346,8 +1259,7 @@ public class HeaderList {
 
 		Header first = null;
 		List<Header> rest = null;
-		for (int i = 0; i < entries.length; i++) {
-			Header x = entries[i];
+		for (Header x : this) {
 			if (eq(x.getName(), name)) {
 				if (first == null)
 					first = x;
@@ -1401,11 +1313,10 @@ public class HeaderList {
 	}
 
 	/**
-	 * Gets all of the headers with the given name.
+	 * Gets all of the headers contained within this list.
 	 *
 	 * <p>
 	 * The returned array maintains the relative order in which the headers were added.
-	 * Header name comparison is case insensitive.
 	 * Headers with null values are ignored.
 	 * Each call creates a new array not backed by this list.
 	 *
@@ -1413,23 +1324,18 @@ public class HeaderList {
 	 * As a general rule, it's more efficient to use the other methods with consumers to
 	 * get headers.
 	 *
-	 * @param name The header name.
-	 *
-	 * @return An array containing all matching headers, never <jk>null</jk>.
+	 * @return An array of all the headers in this list, never <jk>null</jk>.
 	 */
-	public Header[] getAll(String name) {
-		ArrayBuilder<Header> b = ArrayBuilder.of(Header.class).filter(NOT_NULL);
-		for (int i = 0; i < entries.length; i++)
-			if (eq(entries[i].getName(), name))
-				b.add(entries[i]);
-		return b.orElse(EMPTY_ARRAY);
+	public Header[] getAll() {
+		return size() == 0 ? EMPTY_ARRAY : toArray(new Header[size()]);
 	}
 
 	/**
-	 * Gets all of the headers contained within this list.
+	 * Gets all of the headers with the given name.
 	 *
 	 * <p>
 	 * The returned array maintains the relative order in which the headers were added.
+	 * Header name comparison is case insensitive.
 	 * Headers with null values are ignored.
 	 * Each call creates a new array not backed by this list.
 	 *
@@ -1437,19 +1343,16 @@ public class HeaderList {
 	 * As a general rule, it's more efficient to use the other methods with consumers to
 	 * get headers.
 	 *
-	 * @return An array of all the headers in this list, never <jk>null</jk>.
-	 */
-	public Header[] getAll() {
-		return entries.length == 0 ? EMPTY_ARRAY : Arrays.copyOf(entries, entries.length);
-	}
-
-	/**
-	 * Returns the number of headers in this list.
+	 * @param name The header name.
 	 *
-	 * @return The number of headers in this list.
+	 * @return An array containing all matching headers, never <jk>null</jk>.
 	 */
-	public int size() {
-		return entries.length;
+	public Header[] getAll(String name) {
+		ArrayBuilder<Header> b = ArrayBuilder.of(Header.class).filter(NOT_NULL);
+		for (Header x : this)
+			if (eq(x.getName(), name))
+				b.add(x);
+		return b.orElse(EMPTY_ARRAY);
 	}
 
 	/**
@@ -1462,8 +1365,8 @@ public class HeaderList {
 	 * @return The first matching header, or <jk>null</jk> if not found.
 	 */
 	public Optional<Header> getFirst(String name) {
-		for (int i = 0; i < entries.length; i++) {
-			Header x = entries[i];
+		for (int i = 0; i < size(); i++) {
+			Header x = get(i);
 			if (eq(x.getName(), name))
 				return optional(x);
 		}
@@ -1480,8 +1383,8 @@ public class HeaderList {
 	 * @return The last matching header, or <jk>null</jk> if not found.
 	 */
 	public Optional<Header> getLast(String name) {
-		for (int i = entries.length - 1; i >= 0; i--) {
-			Header x = entries[i];
+		for (int i = size() - 1; i >= 0; i--) {
+			Header x = get(i);
 			if (eq(x.getName(), name))
 				return optional(x);
 		}
@@ -1532,8 +1435,7 @@ public class HeaderList {
 	 * @return <jk>true</jk> if at least one header with the name is present.
 	 */
 	public boolean contains(String name) {
-		for (int i = 0; i < entries.length; i++) {
-			Header x = entries[i];
+		for (Header x : this) {
 			if (eq(x.getName(), name))
 				return true;
 		}
@@ -1545,8 +1447,8 @@ public class HeaderList {
 	 *
 	 * @return A new iterator over this list of headers.
 	 */
-	public HeaderIterator iterator() {
-		return new BasicHeaderIterator(entries, null, caseSensitive);
+	public HeaderIterator headerIterator() {
+		return new BasicHeaderIterator(getAll(), null, caseSensitive);
 	}
 
 	/**
@@ -1556,22 +1458,8 @@ public class HeaderList {
 	 *
 	 * @return A new iterator over the matching headers in this list.
 	 */
-	public HeaderIterator iterator(String name) {
-		return new BasicHeaderIterator(entries, name, caseSensitive);
-	}
-
-	/**
-	 * Performs an action on all headers in this list.
-	 *
-	 * <p>
-	 * This is the preferred method for iterating over headers as it does not involve
-	 * creation or copy of lists/arrays.
-	 *
-	 * @param action An action to perform on each element.
-	 * @return This object.
-	 */
-	public HeaderList forEach(Consumer<Header> action) {
-		return forEach(x -> true, action);
+	public HeaderIterator headerIterator(String name) {
+		return new BasicHeaderIterator(getAll(name), name, caseSensitive);
 	}
 
 	/**
@@ -1601,23 +1489,10 @@ public class HeaderList {
 	 * @return This object.
 	 */
 	public HeaderList forEach(Predicate<Header> filter, Consumer<Header> action) {
-		for (int i = 0; i < entries.length; i++)
-			consume(filter, action, entries[i]);
+		forEach(x -> consume(filter, action, x));
 		return this;
 	}
 
-	/**
-	 * Returns a stream of the headers in this list.
-	 *
-	 * <p>
-	 * This does not involve a copy of the underlying array of <c>Header</c> objects so should perform well.
-	 *
-	 * @return This object.
-	 */
-	public Stream<Header> stream() {
-		return Arrays.stream(entries);
-	}
-
 	/**
 	 * Returns a stream of the headers in this list with the specified name.
 	 *
@@ -1637,6 +1512,6 @@ public class HeaderList {
 
 	@Override /* Object */
 	public String toString() {
-		return alist(entries).toString();
+		return super.toString();
 	}
 }
diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartIterator.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartIterator.java
index a93b8b540..d1445b3d7 100644
--- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartIterator.java
+++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartIterator.java
@@ -24,13 +24,4 @@ import org.apache.http.*;
  * 	<li class='extlink'>{@source}
  * </ul>
  */
-public interface PartIterator extends Iterator<NameValuePair> {
-
-	/**
-	 * Indicates whether there is another part in this iteration.
-	 *
-	 * @return <jk>true</jk> if there is another part.
-	 */
-	@Override /* Iterator */
-	boolean hasNext();
-}
+public interface PartIterator extends Iterator<NameValuePair> {}
diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java
index e57bc67c3..db0300d93 100644
--- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java
+++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java
@@ -91,7 +91,7 @@ import org.apache.juneau.svl.*;
  * Various methods are provided for iterating over the parts in this list to avoid array copies.
  * <ul class='javatree'>
  * 	<li class='jm'>{@link #forEach(Consumer)} / {@link #forEach(String,Consumer)} / {@link #forEach(Predicate,Consumer)} - Use consumers to process parts.
- * 	<li class='jm'>{@link #iterator()} / {@link #iterator(String)} - Use an {@link PartIterator} to process parts.
+ * 	<li class='jm'>{@link #partIterator()} / {@link #partIterator(String)} - Use an {@link PartIterator} to process parts.
  * 	<li class='jm'>{@link #stream()} / {@link #stream(String)} - Use a stream.
  * </ul>
  * <p>
@@ -169,8 +169,8 @@ public class PartList extends ControlledArrayList<NameValuePair> {
 	private static final Predicate<NameValuePair> NOT_NULL = x -> x != null;
 
 	/** Represents no part supplier in annotations. */
-	public static final class Null extends PartList {
-		Null(boolean modifiable) {
+	public static final class Void extends PartList {
+		Void() {
 			super(false);
 		}
 		private static final long serialVersionUID = 1L;
@@ -462,19 +462,6 @@ public class PartList extends ControlledArrayList<NameValuePair> {
 			return this;
 		}
 
-		/**
-		 * Adds the specified part to the beginning of the parts in this builder.
-		 *
-		 * @param value The part to add.  <jk>null</jk> values are ignored.
-		 * @return This object.
-		 */
-		@FluentSetter
-		public Builder prepend(PartList value) {
-			if (value != null)
-				prependAll(entries, value.getAll());
-			return this;
-		}
-
 		/**
 		 * Adds the specified part to the beginning of the parts in this builder.
 		 *
@@ -1350,7 +1337,8 @@ public class PartList extends ControlledArrayList<NameValuePair> {
 	 * @return The first matching part, or <jk>null</jk> if not found.
 	 */
 	public Optional<NameValuePair> getFirst(String name) {
-		for (NameValuePair x : this) {
+		for (int i = 0; i < size(); i++) {
+			NameValuePair x = get(i);
 			if (eq(x.getName(), name))
 				return optional(x);
 		}
@@ -1431,8 +1419,7 @@ public class PartList extends ControlledArrayList<NameValuePair> {
 	 *
 	 * @return A new iterator over this list of parts.
 	 */
-	@Override
-	public PartIterator iterator() {
+	public PartIterator partIterator() {
 		return new BasicPartIterator(getAll(), null, caseInsensitive);
 	}
 
@@ -1443,7 +1430,7 @@ public class PartList extends ControlledArrayList<NameValuePair> {
 	 *
 	 * @return A new iterator over the matching parts in this list.
 	 */
-	public PartIterator iterator(String name) {
+	public PartIterator partIterator(String name) {
 		return new BasicPartIterator(getAll(), name, caseInsensitive);
 	}
 
diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpException.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpException.java
index 24f28c679..c7b2fb343 100644
--- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpException.java
+++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpException.java
@@ -310,12 +310,12 @@ public class BasicHttpException extends BasicRuntimeException implements HttpRes
 
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator() {
-		return headers().iterator();
+		return headers().headerIterator();
 	}
 
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator(String name) {
-		return headers().iterator(name);
+		return headers().headerIterator(name);
 	}
 
 	@SuppressWarnings("deprecation")
diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpResponse.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpResponse.java
index af0d0c64c..9cebf3c0b 100644
--- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpResponse.java
+++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/response/BasicHttpResponse.java
@@ -185,12 +185,12 @@ public class BasicHttpResponse implements HttpResponse {
 
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator() {
-		return headers().iterator();
+		return headers().headerIterator();
 	}
 
 	@Override /* HttpMessage */
 	public HeaderIterator headerIterator(String name) {
-		return headers().iterator(name);
+		return headers().headerIterator(name);
 	}
 
 	@SuppressWarnings("deprecation")
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/header/HeaderList_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/header/HeaderList_Test.java
index fbbdbc121..cad508411 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/header/HeaderList_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/header/HeaderList_Test.java
@@ -74,10 +74,10 @@ public class HeaderList_Test {
 		HeaderList x;
 
 		x = headerList(FOO_1, FOO_2, null);
-		assertObject(x).isString("[Foo: 1, Foo: 2]");
+		assertObject(x).isString("[Foo: 1, Foo: 2, null]");
 
 		x = headerList(alist(FOO_1, FOO_2, null));
-		assertObject(x).isString("[Foo: 1, Foo: 2]");
+		assertObject(x).isString("[Foo: 1, Foo: 2, null]");
 
 		x = headerList("Foo","1","Foo","2");
 		assertObject(x).isString("[Foo: 1, Foo: 2]");
@@ -219,18 +219,18 @@ public class HeaderList_Test {
 	@Test
 	public void a12_headerIterator_all() {
 		HeaderList x = HeaderList.of();
-		assertBoolean(x.iterator().hasNext()).isFalse();
+		assertBoolean(x.headerIterator().hasNext()).isFalse();
 		x = HeaderList.of(FOO_1);
-		assertBoolean(x.iterator().hasNext()).isTrue();
+		assertBoolean(x.headerIterator().hasNext()).isTrue();
 	}
 
 	@Test
 	public void a13_headerIterator_single() {
 		HeaderList x = HeaderList.of();
-		assertBoolean(x.iterator("Foo").hasNext()).isFalse();
+		assertBoolean(x.headerIterator("Foo").hasNext()).isFalse();
 		x = HeaderList.of(FOO_1);
-		assertBoolean(x.iterator("Foo").hasNext()).isTrue();
-		assertBoolean(x.iterator("FOO").hasNext()).isTrue();
+		assertBoolean(x.headerIterator("Foo").hasNext()).isTrue();
+		assertBoolean(x.headerIterator("FOO").hasNext()).isTrue();
 	}
 
 	@Test
@@ -445,27 +445,27 @@ public class HeaderList_Test {
 	public void c01_iterators() {
 		HeaderList x = HeaderList.of(Accept.TEXT_XML,ContentType.TEXT_XML);
 
-		HeaderIterator i1 = x.iterator();
+		HeaderIterator i1 = x.headerIterator();
 		assertObject(i1.nextHeader()).isString("Accept: text/xml");
 		assertObject(i1.nextHeader()).isString("Content-Type: text/xml");
 		assertThrown(()->i1.nextHeader()).asMessage().is("Iteration already finished.");
 
-		HeaderIterator i2 = x.iterator();
+		HeaderIterator i2 = x.headerIterator();
 		assertObject(i2.next()).isString("Accept: text/xml");
 		assertObject(i2.nextHeader()).isString("Content-Type: text/xml");
 		assertThrown(()->i2.next()).asMessage().is("Iteration already finished.");
 
-		HeaderIterator i3 = x.iterator("accept");
+		HeaderIterator i3 = x.headerIterator("accept");
 		assertObject(i3.nextHeader()).isString("Accept: text/xml");
 		assertThrown(()->i3.nextHeader()).asMessage().is("Iteration already finished.");
 
 		HeaderList x2 = HeaderList.create().append(Accept.TEXT_XML,ContentType.TEXT_XML).caseSensitive().build();
 
-		HeaderIterator i4 = x2.iterator("Accept");
+		HeaderIterator i4 = x2.headerIterator("Accept");
 		assertObject(i4.nextHeader()).isString("Accept: text/xml");
 		assertThrown(()->i4.nextHeader()).asMessage().is("Iteration already finished.");
 
-		HeaderIterator i5 = x2.iterator("accept");
+		HeaderIterator i5 = x2.headerIterator("accept");
 		assertThrown(()->i5.nextHeader()).asMessage().is("Iteration already finished.");
 
 		assertThrown(()->i5.remove()).asMessage().is("Not supported.");
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java
index bb2fe44f1..c359f78af 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java
@@ -92,7 +92,7 @@ public class PartList_Test {
 		x.append((List<NameValuePair>)null);
 		assertObject(x.build()).isString("Foo=1&Foo=2&Foo=3&Foo=4&Foo=5&Foo=6&Foo=7");
 
-		assertObject(new PartList.Null(false)).isString("");
+		assertObject(new PartList.Void()).isString("");
 	}
 
 	@Test
@@ -241,20 +241,20 @@ public class PartList_Test {
 	}
 
 	@Test
-	public void a12_headerIterator_all() {
+	public void a12_partIterator_all() {
 		PartList x = PartList.of();
-		assertBoolean(x.iterator().hasNext()).isFalse();
+		assertBoolean(x.partIterator().hasNext()).isFalse();
 		x = PartList.of(FOO_1);
-		assertBoolean(x.iterator().hasNext()).isTrue();
+		assertBoolean(x.partIterator().hasNext()).isTrue();
 	}
 
 	@Test
-	public void a13_headerIterator_single() {
+	public void a13_partIterator_single() {
 		PartList x = PartList.of();
-		assertBoolean(x.iterator("Foo").hasNext()).isFalse();
+		assertBoolean(x.partIterator("Foo").hasNext()).isFalse();
 		x = PartList.of(FOO_1);
-		assertBoolean(x.iterator("Foo").hasNext()).isTrue();
-		assertBoolean(x.iterator("FOO").hasNext()).isFalse();
+		assertBoolean(x.partIterator("Foo").hasNext()).isTrue();
+		assertBoolean(x.partIterator("FOO").hasNext()).isFalse();
 	}
 
 	@Test
@@ -473,26 +473,26 @@ public class PartList_Test {
 	public void c01_iterators() {
 		PartList x = PartList.of(APart.X, BPart.X);
 
-		PartIterator i1 = x.iterator();
+		PartIterator i1 = x.partIterator();
 		assertObject(i1.next()).isString("a=x");
 		assertObject(i1.next()).isString("b=x");
 		assertThrown(()->i1.next()).asMessage().is("Iteration already finished.");
 
-		PartIterator i2 = x.iterator();
+		PartIterator i2 = x.partIterator();
 		assertObject(i2.next()).isString("a=x");
 		assertObject(i2.next()).isString("b=x");
 		assertThrown(()->i2.next()).asMessage().is("Iteration already finished.");
 
-		PartIterator i3 = x.iterator("a");
+		PartIterator i3 = x.partIterator("a");
 		assertObject(i3.next()).isString("a=x");
 		assertThrown(()->i3.next()).asMessage().is("Iteration already finished.");
 
-		PartIterator i4 = x.iterator("A");
+		PartIterator i4 = x.partIterator("A");
 		assertThrown(()->i4.next()).asMessage().is("Iteration already finished.");
 
 		PartList x2 = PartList.create().append(APart.X,BPart.X).caseInsensitive().build();
 
-		PartIterator i5 = x2.iterator("A");
+		PartIterator i5 = x2.partIterator("A");
 		assertObject(i5.next()).isString("a=x");
 		assertThrown(()->i5.next()).asMessage().is("Iteration already finished.");
 
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java
index c05218f1d..7c8675711 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java
@@ -567,6 +567,7 @@ public class Remote_Test {
 		String[] getHeaders();
 	}
 
+	@SuppressWarnings("serial")
 	public static class F1b extends HeaderList {
 		public F1b() {
 			super(
@@ -590,8 +591,10 @@ public class Remote_Test {
 		String[] getHeaders();
 	}
 
+	@SuppressWarnings("serial")
 	public static class F2b extends HeaderList {
 		public F2b() {
+			super(true);
 			throw new NullPointerException("foo");
 		}
 	}