org.glassfish.jersey.message.internal.MessageHeaderMethods Maven / Gradle / Ivy
Show all versions of jaxrs-ri Show documentation
/*
* Copyright (c) 2024 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.message.internal;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.ext.RuntimeDelegate;
import java.net.URI;
import java.text.ParseException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Common header methods for outbound and inbound messages.
*/
public abstract class MessageHeaderMethods {
protected RuntimeDelegate runtimeDelegateDecorator;
protected MessageHeaderMethods(Configuration configuration) {
this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
}
protected MessageHeaderMethods(MessageHeaderMethods other) {
this.runtimeDelegateDecorator = other.runtimeDelegateDecorator;
}
/**
* Get a message header as a single string value.
*
* Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
* is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
* class or using its {@code toString} method if a header delegate is not available.
*
* @param name the message header.
* @return the message header value. If the message header is not present then {@code null} is returned. If the message
* header is present but has no value then the empty string is returned. If the message header is present more than once
* then the values of joined together and separated by a ',' character.
*/
public abstract String getHeaderString(String name);
/**
* Get the mutable message headers multivalued map.
*
* @return mutable multivalued map of message headers.
*/
public abstract MultivaluedMap getHeaders();
/**
* Return {@link HeaderValueException.Context} type of the message context.
* @return {@link HeaderValueException.Context} type of the message context.
*/
protected abstract HeaderValueException.Context getHeaderValueExceptionContext();
/**
* Get the links attached to the message as header.
*
* @return links, may return empty {@link java.util.Set} if no links are present. Never
* returns {@code null}.
*/
public abstract Set getLinks();
/**
* Checks whether a header with a specific name and value (or item of the token-separated value list) exists.
*
* Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
* is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
* class or using its {@code toString} method if a header delegate is not available.
*
*
* For example: {@code containsHeaderString("cache-control", ",", "no-store"::equalsIgnoreCase)} will return {@code true} if
* a {@code Cache-Control} header exists that has the value {@code no-store}, the value {@code No-Store} or the value
* {@code Max-Age, NO-STORE, no-transform}, but {@code false} when it has the value {@code no-store;no-transform}
* (missing comma), or the value {@code no - store} (whitespace within value).
*
* @param name the message header.
* @param valueSeparatorRegex Separates the header value into single values. {@code null} does not split.
* @param valuePredicate value must fulfil this predicate.
* @return {@code true} if and only if a header with the given name exists, having either a whitespace-trimmed value
* matching the predicate, or having at least one whitespace-trimmed single value in a token-separated list of single values.
*/
public boolean containsHeaderString(String name, String valueSeparatorRegex, Predicate valuePredicate) {
final String header = getHeaderString(name);
if (header == null) {
return false;
}
final String[] split = header.split(valueSeparatorRegex);
for (String s : split) {
if (valuePredicate.test(s.trim())) {
return true;
}
}
return false;
}
/**
* Checks whether a header with a specific name and value (or item of the comma-separated value list) exists.
*
* Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
* is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
* class or using its {@code toString} method if a header delegate is not available.
*
*
* For example: {@code containsHeaderString("cache-control", "no-store"::equalsIgnoreCase)} will return {@code true} if
* a {@code Cache-Control} header exists that has the value {@code no-store}, the value {@code No-Store} or the value
* {@code Max-Age, NO-STORE, no-transform}, but {@code false} when it has the value {@code no-store;no-transform}
* (missing comma), or the value {@code no - store} (whitespace within value).
*
* @param name the message header.
* @param valuePredicate value must fulfil this predicate.
* @return {@code true} if and only if a header with the given name exists, having either a whitespace-trimmed value
* matching the predicate, or having at least one whitespace-trimmed single value in a comma-separated list of single values.
*/
public boolean containsHeaderString(String name, Predicate valuePredicate) {
return containsHeaderString(name, ",", valuePredicate);
}
/**
* Get the allowed HTTP methods from the Allow HTTP header.
*
* @return the allowed HTTP methods, all methods will returned as upper case
* strings.
*/
public Set getAllowedMethods() {
final String allowed = getHeaderString(HttpHeaders.ALLOW);
if (allowed == null || allowed.isEmpty()) {
return Collections.emptySet();
}
try {
return new HashSet(HttpHeaderReader.readStringList(allowed.toUpperCase(Locale.ROOT)));
} catch (java.text.ParseException e) {
throw exception(HttpHeaders.ALLOW, allowed, e);
}
}
/**
* Get message date.
*
* @return the message date, otherwise {@code null} if not present.
*/
public Date getDate() {
return singleHeader(HttpHeaders.DATE, Date.class, input -> {
try {
return HttpHeaderReader.readDate(input);
} catch (ParseException e) {
throw new ProcessingException(e);
}
}, false);
}
/**
* Get the entity tag.
*
* @return the entity tag, otherwise {@code null} if not present.
*/
public EntityTag getEntityTag() {
return singleHeader(HttpHeaders.ETAG, EntityTag.class, new Function() {
@Override
public EntityTag apply(String value) {
try {
return value == null ? null : EntityTag.valueOf(value);
} catch (IllegalArgumentException ex) {
throw new ProcessingException(ex);
}
}
}, false);
}
/**
* Get the language of the entity.
*
* @return the language of the entity or {@code null} if not specified
*/
public Locale getLanguage() {
return singleHeader(HttpHeaders.CONTENT_LANGUAGE, Locale.class, input -> {
try {
return new LanguageTag(input).getAsLocale();
} catch (ParseException e) {
throw new ProcessingException(e);
}
}, false);
}
/**
* Get the last modified date.
*
* @return the last modified date, otherwise {@code null} if not present.
*/
public Date getLastModified() {
return singleHeader(HttpHeaders.LAST_MODIFIED, Date.class, new Function() {
@Override
public Date apply(String input) {
try {
return HttpHeaderReader.readDate(input);
} catch (ParseException e) {
throw new ProcessingException(e);
}
}
}, false);
}
/**
* Get Content-Length value.
*
* Note: {@link #getLengthLong() getLengthLong()}
* should be preferred over this method, since it returns a {@code long}
* instead and is therefore more portable.
*
* @return Content-Length as a postive integer if present and valid number, {@code -1} if negative number.
* @throws ProcessingException when {@link Integer#parseInt(String)} (String)} throws {@link NumberFormatException}.
*/
public int getLength() {
return singleHeader(HttpHeaders.CONTENT_LENGTH, Integer.class, input -> {
try {
if (input != null && !input.isEmpty()) {
int i = Integer.parseInt(input);
if (i >= 0) {
return i;
}
}
return -1;
} catch (NumberFormatException ex) {
throw new ProcessingException(ex);
}
}, true);
}
/**
* Get Content-Length value.
*
* @return Content-Length as a positive long if present and valid number, {@code -1} if negative number.
* @throws ProcessingException when {@link Long#parseLong(String)} throws {@link NumberFormatException}.
*/
public long getLengthLong() {
return singleHeader(HttpHeaders.CONTENT_LENGTH, Long.class, input -> {
try {
if (input != null && !input.isEmpty()) {
long l = Long.parseLong(input);
if (l >= 0) {
return l;
}
}
return -1L;
} catch (NumberFormatException ex) {
throw new ProcessingException(ex);
}
}, true);
}
/**
* Get the link for the relation.
*
* @param relation link relation.
* @return the link for the relation, otherwise {@code null} if not present.
*/
public Link getLink(String relation) {
for (Link link : getLinks()) {
List relations = LinkProvider.getLinkRelations(link.getRel());
if (relations != null && relations.contains(relation)) {
return link;
}
}
return null;
}
/**
* Convenience method that returns a {@link javax.ws.rs.core.Link.Builder Link.Builder}
* for the relation.
*
* @param relation link relation.
* @return the link builder for the relation, otherwise {@code null} if not
* present.
*/
public Link.Builder getLinkBuilder(String relation) {
Link link = getLink(relation);
if (link == null) {
return null;
}
return Link.fromLink(link);
}
/**
* Get the location.
*
* @return the location URI, otherwise {@code null} if not present.
*/
public URI getLocation() {
return singleHeader(HttpHeaders.LOCATION, URI.class, value -> {
try {
return value == null ? null : URI.create(value);
} catch (IllegalArgumentException ex) {
throw new ProcessingException(ex);
}
}, false);
}
/**
* Get any cookies that accompanied the message.
*
* @return a read-only map of cookie name (String) to {@link javax.ws.rs.core.Cookie}.
*/
public Map getRequestCookies() {
@SuppressWarnings("unchecked")
final List