/*
* Copyright (c) 2012, 2020 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 java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.text.ParseException;
import java.util.ArrayList;
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.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.EntityTag;
import jakarta.ws.rs.core.GenericEntity;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Link;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.NewCookie;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.util.ReflectionHelper;
Base outbound message context implementation.
Author: Marek Potociar
/**
* Base outbound message context implementation.
*
* @author Marek Potociar
*/
public class OutboundMessageContext {
private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
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 CommittingOutputStream committingOutputStream;
private Configuration configuration;
private Object entity;
private GenericType<?> entityType;
private Annotation[] entityAnnotations = EMPTY_ANNOTATIONS;
private OutputStream entityStream;
The callback interface which is used to get the terminal output stream into which the entity should be
written and to inform the implementation about the entity size.
/**
* The callback interface which is used to get the terminal output stream into which the entity should be
* written and to inform the implementation about the entity size.
*/
public static interface StreamProvider {
Get the output stream. This method will be called after all the writer interceptors
are called and written entity is buffered into the buffer or the buffer exceeds. Params: - contentLength – the size of the buffered entity or -1 if the entity exceeded the maximum buffer
size or if the buffering is disabled.
Throws: - IOException – in case of an IO error.
Returns: the adapted output stream into which the serialized entity should be written. May return null which will cause ignoring the written entity (in that case the entity will still be written by message body writers
but the output will be ignored).
/**
* Get the output stream. This method will be called after all the
* {@link jakarta.ws.rs.ext.WriterInterceptor writer interceptors} are called and written entity is buffered
* into the buffer or the buffer exceeds.
*
* @param contentLength the size of the buffered entity or -1 if the entity exceeded the maximum buffer
* size or if the buffering is disabled.
* @return the adapted output stream into which the serialized entity should be written. May return null
* which will cause ignoring the written entity (in that case the entity will
* still be written by {@link jakarta.ws.rs.ext.MessageBodyWriter message body writers}
* but the output will be ignored).
* @throws java.io.IOException in case of an IO error.
*/
public OutputStream getOutputStream(int contentLength) throws IOException;
}
Create new outbound message context.
Params: - configuration – the client/server
Configuration
. If null
, the default behaviour is expected.
/**
* Create new outbound message context.
* @param configuration the client/server {@link Configuration}. If {@code null}, the default behaviour is expected.
*/
public OutboundMessageContext(Configuration configuration) {
this.configuration = configuration;
this.headers = HeaderUtils.createOutbound();
this.committingOutputStream = new CommittingOutputStream();
this.entityStream = committingOutputStream;
}
Create new outbound message context copying the content
of another context.
Params: - original – the original outbound message context.
/**
* Create new outbound message context copying the content
* of another context.
*
* @param original the original outbound message context.
*/
public OutboundMessageContext(OutboundMessageContext original) {
this.headers = HeaderUtils.createOutbound();
this.headers.putAll(original.headers);
this.committingOutputStream = new CommittingOutputStream();
this.entityStream = committingOutputStream;
this.entity = original.entity;
this.entityType = original.entityType;
this.entityAnnotations = original.entityAnnotations;
this.configuration = original.configuration;
}
Create new outbound message context.
See Also: - OutboundMessageContext(Configuration)
/**
* Create new outbound message context.
*
* @see #OutboundMessageContext(Configuration)
*/
@Deprecated
public OutboundMessageContext() {
this ((Configuration) null);
}
Replace all headers.
Params: - headers – new headers.
/**
* Replace all headers.
*
* @param headers new headers.
*/
public void replaceHeaders(MultivaluedMap<String, Object> headers) {
getHeaders().clear();
if (headers != null) {
getHeaders().putAll(headers);
}
}
Get a multi-valued map representing outbound message headers with their values converted
to strings.
Returns: multi-valued map of outbound message header names to their string-converted values.
/**
* Get a multi-valued map representing outbound message headers with their values converted
* to strings.
*
* @return multi-valued map of outbound message header names to their string-converted values.
*/
public MultivaluedMap<String, String> getStringHeaders() {
return HeaderUtils.asStringHeaders(headers, configuration);
}
Get a message header as a single string value.
Each single header value is converted to String using a HeaderDelegate
if one is available via RuntimeDelegate.createHeaderDelegate(Class<Object>)
for the header value class or using its toString
method if a header delegate is not available.
Params: - name – the message header.
Returns: the message header value. If the message header is not present then 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.
/**
* Get a message header as a single string value.
* <p>
* Each single header value is converted to String using a
* {@link jakarta.ws.rs.ext.RuntimeDelegate.HeaderDelegate} if one is available
* via {@link jakarta.ws.rs.ext.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 String getHeaderString(String name) {
return HeaderUtils.asHeaderString(headers.get(name), RuntimeDelegateDecorator.configured(configuration));
}
Get a single typed header value.
Params: - name – header name.
- valueType – header value class.
- converter – from string conversion function. Is expected to throw
ProcessingException
if conversion fails. - convertNull – if
true
this method calls the provided converter even for null
. Otherwise this method returns the null
without calling the converter.
Type parameters: - <T> – header value type.
Returns: value of the header, or (possibly converted) null
if not present.
/**
* Get a single typed header value.
*
* @param <T> header value type.
* @param name header name.
* @param valueType header value class.
* @param converter from string conversion function. Is expected to throw {@link ProcessingException}
* if conversion fails.
* @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
* method returns the {@code null} without calling the converter.
* @return value of the header, or (possibly converted) {@code null} if not present.
*/
private <T> T singleHeader(String name, Class<T> valueType, Function<String, T> converter, boolean convertNull) {
final List<Object> values = headers.get(name);
if (values == null || values.isEmpty()) {
return convertNull ? converter.apply(null) : null;
}
if (values.size() > 1) {
throw new HeaderValueException(
LocalizationMessages.TOO_MANY_HEADER_VALUES(name, values.toString()),
HeaderValueException.Context.OUTBOUND);
}
Object value = values.get(0);
if (value == null) {
return convertNull ? converter.apply(null) : null;
}
if (valueType.isInstance(value)) {
return valueType.cast(value);
} else {
try {
return converter.apply(HeaderUtils.asString(value, null));
} catch (ProcessingException ex) {
throw exception(name, value, ex);
}
}
}
private static HeaderValueException exception(final String headerName, Object headerValue, Exception e) {
return new HeaderValueException(LocalizationMessages.UNABLE_TO_PARSE_HEADER_VALUE(headerName, headerValue), e,
HeaderValueException.Context.OUTBOUND);
}
Get the mutable message headers multivalued map.
Returns: mutable multivalued map of message headers.
/**
* Get the mutable message headers multivalued map.
*
* @return mutable multivalued map of message headers.
*/
public MultivaluedMap<String, Object> getHeaders() {
return headers;
}
Get message date.
Returns: the message date, otherwise null
if not present.
/**
* 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 language of the entity.
Returns: the language of the entity or null
if not specified
/**
* 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 media type of the entity.
Returns: the media type or null
if not specified (e.g. there's no message entity).
/**
* Get the media type of the entity.
*
* @return the media type or {@code null} if not specified (e.g. there's no
* message entity).
*/
public MediaType getMediaType() {
return singleHeader(HttpHeaders.CONTENT_TYPE, MediaType.class, RuntimeDelegateDecorator.configured(configuration)
.createHeaderDelegate(MediaType.class)::fromString, false);
}
Get a list of media types that are acceptable for the message.
Returns: a read-only list of requested message media types sorted according
to their q-value, with highest preference first.
/**
* Get a list of media types that are acceptable for the message.
*
* @return a read-only list of requested message media types sorted according
* to their q-value, with highest preference first.
*/
@SuppressWarnings("unchecked")
public List<MediaType> getAcceptableMediaTypes() {
final List<Object> values = headers.get(HttpHeaders.ACCEPT);
if (values == null || values.isEmpty()) {
return WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST;
}
final List<MediaType> result = new ArrayList<>(values.size());
boolean conversionApplied = false;
for (final Object value : values) {
try {
if (value instanceof MediaType) {
final AcceptableMediaType _value = AcceptableMediaType.valueOf((MediaType) value);
conversionApplied = _value != value; // true if value was not an instance of AcceptableMediaType already
result.add(_value);
} else {
conversionApplied = true;
result.addAll(HttpHeaderReader.readAcceptMediaType(HeaderUtils.asString(value, configuration)));
}
} catch (java.text.ParseException e) {
throw exception(HttpHeaders.ACCEPT, value, e);
}
}
if (conversionApplied) {
// cache converted
headers.put(HttpHeaders.ACCEPT,
result.stream()
.map((Function<MediaType, Object>) mediaType -> mediaType)
.collect(Collectors.toList()));
}
return Collections.unmodifiableList(result);
}
Get a list of languages that are acceptable for the message.
Returns: a read-only list of acceptable languages sorted according
to their q-value, with highest preference first.
/**
* Get a list of languages that are acceptable for the message.
*
* @return a read-only list of acceptable languages sorted according
* to their q-value, with highest preference first.
*/
public List<Locale> getAcceptableLanguages() {
final List<Object> values = headers.get(HttpHeaders.ACCEPT_LANGUAGE);
if (values == null || values.isEmpty()) {
return Collections.singletonList(new AcceptableLanguageTag("*", null).getAsLocale());
}
final List<Locale> result = new ArrayList<Locale>(values.size());
boolean conversionApplied = false;
for (final Object value : values) {
if (value instanceof Locale) {
result.add((Locale) value);
} else {
conversionApplied = true;
try {
result.addAll(HttpHeaderReader.readAcceptLanguage(HeaderUtils.asString(value, configuration))
.stream()
.map(LanguageTag::getAsLocale)
.collect(Collectors.toList()));
} catch (java.text.ParseException e) {
throw exception(HttpHeaders.ACCEPT_LANGUAGE, value, e);
}
}
}
if (conversionApplied) {
// cache converted
headers.put(HttpHeaders.ACCEPT_LANGUAGE,
result.stream()
.map((Function<Locale, Object>) locale -> locale)
.collect(Collectors.toList()));
}
return Collections.unmodifiableList(result);
}
Get any cookies that accompanied the message.
Returns: a read-only map of cookie name (String) to Cookie
.
/**
* Get any cookies that accompanied the message.
*
* @return a read-only map of cookie name (String) to {@link jakarta.ws.rs.core.Cookie}.
*/
public Map<String, Cookie> getRequestCookies() {
final List<Object> cookies = headers.get(HttpHeaders.COOKIE);
if (cookies == null || cookies.isEmpty()) {
return Collections.emptyMap();
}
Map<String, Cookie> result = new HashMap<String, Cookie>();
for (String cookie : HeaderUtils.asStringList(cookies, configuration)) {
if (cookie != null) {
result.putAll(HttpHeaderReader.readCookies(cookie));
}
}
return result;
}
Get the allowed HTTP methods from the Allow HTTP header.
Returns: the allowed HTTP methods, all methods will returned as upper case
strings.
/**
* 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<String> getAllowedMethods() {
final String allowed = getHeaderString(HttpHeaders.ALLOW);
if (allowed == null || allowed.isEmpty()) {
return Collections.emptySet();
}
try {
return new HashSet<String>(HttpHeaderReader.readStringList(allowed));
} catch (java.text.ParseException e) {
throw exception(HttpHeaders.ALLOW, allowed, e);
}
}
Get Content-Length value.
Note: getLengthLong()
should be preferred over this method, since it returns a long
instead and is therefore more portable.
Throws: - ProcessingException – when
Integer.parseInt(String)
(String)} throws NumberFormatException
.
Returns: Content-Length as a postive integer if present and valid number, -1
if negative number.
/**
* Get Content-Length value.
* <p>
* <B>Note</B>: {@link #getLengthLong() getLengthLong()}
* should be preferred over this method, since it returns a {@code long}
* instead and is therefore more portable.</P>
*
* @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.
Throws: - ProcessingException – when
Long.parseLong(String)
throws NumberFormatException
.
Returns: Content-Length as a positive long if present and valid number, -1
if negative number.
/**
* 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 any new cookies set on the message message.
Returns: a read-only map of cookie name (String) to a new cookie
.
/**
* Get any new cookies set on the message message.
*
* @return a read-only map of cookie name (String) to a {@link jakarta.ws.rs.core.NewCookie new cookie}.
*/
public Map<String, NewCookie> getResponseCookies() {
List<Object> cookies = headers.get(HttpHeaders.SET_COOKIE);
if (cookies == null || cookies.isEmpty()) {
return Collections.emptyMap();
}
Map<String, NewCookie> result = new HashMap<String, NewCookie>();
for (String cookie : HeaderUtils.asStringList(cookies, configuration)) {
if (cookie != null) {
NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
String cookieName = newCookie.getName();
if (result.containsKey(cookieName)) {
result.put(cookieName, HeaderUtils.getPreferredCookie(result.get(cookieName), newCookie));
} else {
result.put(cookieName, newCookie);
}
}
}
return result;
}
Get the entity tag.
Returns: the entity tag, otherwise null
if not present.
/**
* 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<String, EntityTag>() {
@Override
public EntityTag apply(String value) {
try {
return value == null ? null : EntityTag.valueOf(value);
} catch (IllegalArgumentException ex) {
throw new ProcessingException(ex);
}
}
}, false);
}
Get the last modified date.
Returns: the last modified date, otherwise null
if not present.
/**
* 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<String, Date>() {
@Override
public Date apply(String input) {
try {
return HttpHeaderReader.readDate(input);
} catch (ParseException e) {
throw new ProcessingException(e);
}
}
}, false);
}
Get the location.
Returns: the location URI, otherwise null
if not present.
/**
* Get the location.
*
* @return the location URI, otherwise {@code null} if not present.
*/
public URI getLocation() {
return singleHeader(HttpHeaders.LOCATION, URI.class, new Function<String, URI>() {
@Override
public URI apply(String value) {
try {
return value == null ? null : URI.create(value);
} catch (IllegalArgumentException ex) {
throw new ProcessingException(ex);
}
}
}, false);
}
Get the links attached to the message as header.
Returns: links, may return empty Set
if no links are present. Never returns null
.
/**
* 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 Set<Link> getLinks() {
List<Object> values = headers.get(HttpHeaders.LINK);
if (values == null || values.isEmpty()) {
return Collections.emptySet();
}
final Set<Link> result = new HashSet<Link>(values.size());
boolean conversionApplied = false;
for (final Object value : values) {
if (value instanceof Link) {
result.add((Link) value);
} else {
conversionApplied = true;
try {
result.add(Link.valueOf(HeaderUtils.asString(value, configuration)));
} catch (IllegalArgumentException e) {
throw exception(HttpHeaders.LINK, value, e);
}
}
}
if (conversionApplied) {
// cache converted
headers.put(HttpHeaders.LINK,
result.stream()
.map((Function<Link, Object>) link -> link)
.collect(Collectors.toList()));
}
return Collections.unmodifiableSet(result);
}
Check if link for relation exists.
Params: - relation – link relation.
Returns: true
if the for the relation link exists, false
otherwise.
/**
* Check if link for relation exists.
*
* @param relation link relation.
* @return {@code true} if the for the relation link exists, {@code false}
* otherwise.
*/
public boolean hasLink(String relation) {
for (Link link : getLinks()) {
List<String> relations = LinkProvider.getLinkRelations(link.getRel());
if (relations != null && relations.contains(relation)) {
return true;
}
}
return false;
}
Get the link for the relation.
Params: - relation – link relation.
Returns: the link for the relation, otherwise null
if not present.
/**
* 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<String> relations = LinkProvider.getLinkRelations(link.getRel());
if (relations != null && relations.contains(relation)) {
return link;
}
}
return null;
}
Convenience method that returns a Link.Builder
for the relation. Params: - relation – link relation.
Returns: the link builder for the relation, otherwise null
if not present.
/**
* Convenience method that returns a {@link jakarta.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);
}
// Message entity
Check if there is an entity available in the message.
The method returns true
if the entity is present, returns false
otherwise.
Returns: true
if there is an entity present in the message, false
otherwise.
/**
* Check if there is an entity available in the message.
* <p>
* The method returns {@code true} if the entity is present, returns
* {@code false} otherwise.
*
* @return {@code true} if there is an entity present in the message,
* {@code false} otherwise.
*/
public boolean hasEntity() {
return entity != null;
}
Get the message entity Java instance.
Returns null
if the message does not contain an entity.
Returns: the message entity or null
if message does not contain an entity body.
/**
* Get the message entity Java instance.
* <p>
* Returns {@code null} if the message does not contain an entity.
*
* @return the message entity or {@code null} if message does not contain an
* entity body.
*/
public Object getEntity() {
return entity;
}
Set a new message message entity.
Params: - entity – entity object.
See Also:
/**
* Set a new message message entity.
*
* @param entity entity object.
* @see jakarta.ws.rs.ext.MessageBodyWriter
*/
public void setEntity(Object entity) {
setEntity(entity, ReflectionHelper.genericTypeFor(entity));
}
Set a new message message entity.
Params: - entity – entity object.
- annotations – annotations attached to the entity.
See Also:
/**
* Set a new message message entity.
*
* @param entity entity object.
* @param annotations annotations attached to the entity.
* @see jakarta.ws.rs.ext.MessageBodyWriter
*/
public void setEntity(Object entity, Annotation[] annotations) {
setEntity(entity, ReflectionHelper.genericTypeFor(entity));
setEntityAnnotations(annotations);
}
Set a new message message entity.
Params: - entity – entity object.
- type – entity generic type information.
See Also:
/**
* Set a new message message entity.
*
* @param entity entity object.
* @param type entity generic type information.
* @see jakarta.ws.rs.ext.MessageBodyWriter
*/
private void setEntity(Object entity, GenericType<?> type) {
if (entity instanceof GenericEntity) {
this.entity = ((GenericEntity) entity).getEntity();
} else {
this.entity = entity;
}
// ignoring overridden generic entity type information
this.entityType = type;
}
Set a new message message entity.
Params: - entity – entity object.
- type – declared entity class.
- annotations – annotations attached to the entity.
See Also:
/**
* Set a new message message entity.
*
* @param entity entity object.
* @param type declared entity class.
* @param annotations annotations attached to the entity.
* @see jakarta.ws.rs.ext.MessageBodyWriter
*/
public void setEntity(Object entity, Type type, Annotation[] annotations) {
setEntity(entity, new GenericType(type));
setEntityAnnotations(annotations);
}
Set a new message message entity.
Params: - entity – entity object.
- annotations – annotations attached to the entity.
- mediaType – entity media type.
See Also:
/**
* Set a new message message entity.
*
* @param entity entity object.
* @param annotations annotations attached to the entity.
* @param mediaType entity media type.
* @see jakarta.ws.rs.ext.MessageBodyWriter
*/
public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType) {
setEntity(entity, annotations);
setMediaType(mediaType);
}
Set the message content media type.
Params: - mediaType – message content media type.
/**
* Set the message content media type.
*
* @param mediaType message content media type.
*/
public void setMediaType(MediaType mediaType) {
this.headers.putSingle(HttpHeaders.CONTENT_TYPE, mediaType);
}
Get the raw message entity type information.
Returns: raw message entity type information.
/**
* Get the raw message entity type information.
*
* @return raw message entity type information.
*/
public Class<?> getEntityClass() {
return entityType == null ? null : entityType.getRawType();
}
Get the message entity type information.
Returns: message entity type.
/**
* Get the message entity type information.
*
* @return message entity type.
*/
public Type getEntityType() {
return entityType == null ? null : entityType.getType();
}
Set the message entity type information.
This method overrides any computed or previously set entity type information.
Params: - type – overriding message entity type.
/**
* Set the message entity type information.
* <p>
* This method overrides any computed or previously set entity type information.
*
* @param type overriding message entity type.
*/
public void setEntityType(Type type) {
this.entityType = new GenericType(type);
}
Get the annotations attached to the entity.
Returns: entity annotations.
/**
* Get the annotations attached to the entity.
*
* @return entity annotations.
*/
public Annotation[] getEntityAnnotations() {
return entityAnnotations.clone();
}
Set the annotations attached to the entity.
Params: - annotations – entity annotations.
/**
* Set the annotations attached to the entity.
*
* @param annotations entity annotations.
*/
public void setEntityAnnotations(Annotation[] annotations) {
this.entityAnnotations = (annotations == null) ? EMPTY_ANNOTATIONS : annotations;
}
Get the entity output stream.
Returns: entity output stream.
/**
* Get the entity output stream.
*
* @return entity output stream.
*/
public OutputStream getEntityStream() {
return entityStream;
}
Set a new entity output stream.
Params: - outputStream – new entity output stream.
/**
* Set a new entity output stream.
*
* @param outputStream new entity output stream.
*/
public void setEntityStream(OutputStream outputStream) {
this.entityStream = outputStream;
}
Enable a buffering of serialized entity. The buffering will be configured from configuration. The property determining the size of the buffer is CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER
. The buffering functionality is by default disabled and could be enabled by calling this method. In this case this method must be called before first bytes are written to the entity stream
. Params: - configuration – runtime configuration.
/**
* Enable a buffering of serialized entity. The buffering will be configured from configuration. The property
* determining the size of the buffer is {@link CommonProperties#OUTBOUND_CONTENT_LENGTH_BUFFER}.
* </p>
* The buffering functionality is by default disabled and could be enabled by calling this method. In this case
* this method must be called before first bytes are written to the {@link #getEntityStream() entity stream}.
*
* @param configuration runtime configuration.
*/
public void enableBuffering(Configuration configuration) {
final Integer bufferSize = CommonProperties.getValue(configuration.getProperties(),
configuration.getRuntimeType(), CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, Integer.class);
if (bufferSize != null) {
committingOutputStream.enableBuffering(bufferSize);
} else {
committingOutputStream.enableBuffering();
}
}
Set a stream provider callback.
This method must be called before first bytes are written to the entity stream
. Params: - streamProvider – non-
null
output stream provider.
/**
* Set a stream provider callback.
* <p/>
* This method must be called before first bytes are written to the {@link #getEntityStream() entity stream}.
*
* @param streamProvider non-{@code null} output stream provider.
*/
public void setStreamProvider(StreamProvider streamProvider) {
committingOutputStream.setStreamProvider(streamProvider);
}
Commits the entity stream
if it wasn't already committed. Throws: - IOException – in case of the IO error.
/**
* Commits the {@link #getEntityStream() entity stream} if it wasn't already committed.
*
* @throws IOException in case of the IO error.
*/
public void commitStream() throws IOException {
if (!committingOutputStream.isCommitted()) {
entityStream.flush();
if (!committingOutputStream.isCommitted()) {
committingOutputStream.commit();
committingOutputStream.flush();
}
}
}
Returns true
if the entity stream has been committed. Returns: true
if the entity stream has been committed. Otherwise returns false
.
/**
* Returns {@code true} if the entity stream has been committed.
*
* @return {@code true} if the entity stream has been committed. Otherwise returns {@code false}.
*/
public boolean isCommitted() {
return committingOutputStream.isCommitted();
}
Closes the context. Flushes and closes the entity stream.
/**
* Closes the context. Flushes and closes the entity stream.
*/
public void close() {
if (hasEntity()) {
try {
final OutputStream es = getEntityStream();
es.flush();
es.close();
} catch (IOException e) {
// Happens when the client closed connection before receiving the full response.
// This is OK and not interesting in vast majority of the cases
// hence the log level set to FINE to make sure it does not flood the log unnecessarily
// (especially for clients disconnecting from SSE listening, which is very common).
Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e);
} finally {
// In case some of the output stream wrapper does not delegate close() call we
// close the root stream manually to make sure it commits the data.
if (!committingOutputStream.isClosed()) {
try {
committingOutputStream.close();
} catch (IOException e) {
// Just log the exception
Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e);
}
}
}
}
}
void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
The related client/server side Configuration
. Can be null
. Returns: Configuration
the configuration
/**
* The related client/server side {@link Configuration}. Can be {@code null}.
* @return {@link Configuration} the configuration
*/
public Configuration getConfiguration() {
return configuration;
}
}