All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jersey.message.internal.OutboundJaxrsResponse Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.9
Show newest version
/*
 * 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.core.CacheControl;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.EntityTag;
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 jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Variant;

import org.glassfish.jersey.internal.LocalizationMessages;

/**
 * An outbound JAX-RS response message.
 *
 * The implementation delegates method calls to an {@link #getContext() underlying
 * outbound message context}.
 *
 * @author Marek Potociar
 */
public class OutboundJaxrsResponse extends jakarta.ws.rs.core.Response {

    private final OutboundMessageContext context;
    private final StatusType status;

    private boolean closed = false;
    private boolean buffered = false;

    /**
     * Get an OutboundJaxrsResponse instance for a given JAX-RS response.
     *
     * @param response response instance to from.
     * @param configuration the related client/server side {@link Configuration}. Can be {@code null}.
     * @return corresponding {@code OutboundJaxrsResponse} instance.
     */
    public static OutboundJaxrsResponse from(Response response, Configuration configuration) {
        if (response instanceof OutboundJaxrsResponse) {
             // RuntimeDelegate.getInstance().createResponseBuilder() does not set configuration
            ((OutboundJaxrsResponse) response).context.setConfiguration(configuration);
            return (OutboundJaxrsResponse) response;
        } else {
            final StatusType status = response.getStatusInfo();
            final OutboundMessageContext context = new OutboundMessageContext(configuration);
            context.getHeaders().putAll(response.getMetadata());
            context.setEntity(response.getEntity());
            return new OutboundJaxrsResponse(status, context);
        }
    }

    /**
     * Get an OutboundJaxrsResponse instance for a given JAX-RS response.
     *
     * @param response response instance to from.
     * @return corresponding {@code OutboundJaxrsResponse} instance.
     * @see #from(Response, Configuration)
     */
    @Deprecated
    public static OutboundJaxrsResponse from(Response response) {
        return from(response, (Configuration) null);
    }

    /**
     * Create new outbound JAX-RS response message instance.
     *
     * @param status  response status.
     * @param context underlying outbound message context.
     */
    public OutboundJaxrsResponse(StatusType status, OutboundMessageContext context) {
        this.status = status;
        this.context = context;
    }

    /**
     * Get the underlying outbound message context.
     *
     * @return underlying outbound message context.
     */
    public OutboundMessageContext getContext() {
        return context;
    }

    @Override
    public int getStatus() {
        return status.getStatusCode();
    }

    @Override
    public StatusType getStatusInfo() {
        return status;
    }

    @Override
    public Object getEntity() {
        if (closed) {
            throw new IllegalStateException(LocalizationMessages.RESPONSE_CLOSED());
        }
        return context.getEntity();
    }

    @Override
    public  T readEntity(Class type) throws ProcessingException {
        throw new IllegalStateException(LocalizationMessages.NOT_SUPPORTED_ON_OUTBOUND_MESSAGE());
    }

    @Override
    public  T readEntity(GenericType entityType) throws ProcessingException {
        throw new IllegalStateException(LocalizationMessages.NOT_SUPPORTED_ON_OUTBOUND_MESSAGE());
    }

    @Override
    public  T readEntity(Class type, Annotation[] annotations) throws ProcessingException {
        throw new IllegalStateException(LocalizationMessages.NOT_SUPPORTED_ON_OUTBOUND_MESSAGE());
    }

    @Override
    public  T readEntity(GenericType entityType, Annotation[] annotations) throws ProcessingException {
        throw new IllegalStateException(LocalizationMessages.NOT_SUPPORTED_ON_OUTBOUND_MESSAGE());
    }

    @Override
    public boolean hasEntity() {
        if (closed) {
            throw new IllegalStateException(LocalizationMessages.RESPONSE_CLOSED());
        }
        return context.hasEntity();
    }

    @Override
    public boolean bufferEntity() throws ProcessingException {
        if (closed) {
            throw new IllegalStateException(LocalizationMessages.RESPONSE_CLOSED());
        }

        if (!context.hasEntity() || !InputStream.class.isAssignableFrom(context.getEntityClass())) {
            return false;
        }

        if (buffered) {
            // already buffered
            return true;
        }
        final InputStream in = InputStream.class.cast(context.getEntity());
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        } catch (IOException ex) {
            throw new ProcessingException(ex);
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                throw new ProcessingException(ex);
            }
        }

        context.setEntity(new ByteArrayInputStream(out.toByteArray()));
        buffered = true;
        return true;
    }

    @Override
    public void close() throws ProcessingException {
        closed = true;
        context.close();
        if (buffered) {
            // release buffer
            context.setEntity(null);
        } else if (context.hasEntity() && InputStream.class.isAssignableFrom(context.getEntityClass())) {
            try {
                InputStream.class.cast(context.getEntity()).close();
            } catch (IOException ex) {
                throw new ProcessingException(ex);
            }
        }
    }

    @Override
    public MultivaluedMap getStringHeaders() {
        return context.getStringHeaders();
    }

    @Override
    public String getHeaderString(String name) {
        return context.getHeaderString(name);
    }

    @Override
    public MediaType getMediaType() {
        return context.getMediaType();
    }

    @Override
    public Locale getLanguage() {
        return context.getLanguage();
    }

    @Override
    public int getLength() {
        return context.getLength();
    }

    @Override
    public Map getCookies() {
        return context.getResponseCookies();
    }

    @Override
    public EntityTag getEntityTag() {
        return context.getEntityTag();
    }

    @Override
    public Date getDate() {
        return context.getDate();
    }

    @Override
    public Date getLastModified() {
        return context.getLastModified();
    }

    @Override
    public Set getAllowedMethods() {
        return context.getAllowedMethods();
    }

    @Override
    public URI getLocation() {
        return context.getLocation();
    }

    @Override
    public Set getLinks() {
        return context.getLinks();
    }

    @Override
    public boolean hasLink(String relation) {
        return context.hasLink(relation);
    }

    @Override
    public Link getLink(String relation) {
        return context.getLink(relation);
    }

    @Override
    public Link.Builder getLinkBuilder(String relation) {
        return context.getLinkBuilder(relation);
    }

    @Override
    @SuppressWarnings("unchecked")
    public MultivaluedMap getMetadata() {
        return context.getHeaders();
    }

    @Override
    public String toString() {
        return "OutboundJaxrsResponse{"
                + "status=" + status.getStatusCode()
                + ", reason=" + status.getReasonPhrase()
                + ", hasEntity=" + context.hasEntity()
                + ", closed=" + closed
                + ", buffered=" + buffered + "}";
    }

    /**
     * Outbound JAX-RS {@code Response.ResponseBuilder} implementation.
     *
     * The implementation delegates method calls to an {@link #getContext() underlying
     * outbound message context}. Upon a call to a {@link #build()} method
     * a new instance of {@link OutboundJaxrsResponse} is produced.
     */
    public static class Builder extends ResponseBuilder {

        private StatusType status;
        private final OutboundMessageContext context;

        /* thread-local storage for request baseUri for use in the response headers */
        private static final InheritableThreadLocal baseUriThreadLocal = new InheritableThreadLocal();

        /**
         * Set the {@code baseUri} of the actual request into the {@link InheritableThreadLocal}.
         * 

* The {@code baseUri} will be used for absolutizing the location header * content in case that only a relative URI is provided. *

*

* After resource method invocation when the value is not needed * any more to be stored in {@code ThreadLocal} {@link #clearBaseUri() clearBaseUri()} should be * called for cleanup in order to prevent possible memory leaks. *

* * @param baseUri - baseUri of the actual request * @see #location(java.net.URI) * @since 2.4 */ public static void setBaseUri(URI baseUri) { baseUriThreadLocal.set(baseUri); } /** * Return request baseUri previously set by {@link #setBaseUri(java.net.URI)}. * * Returned {@link URI} is used for absolutization of the location header in case that only a relative * {@code URI} was provided. * * @return baseUri of the actual request * @see #location(java.net.URI) * @since 2.4 */ private static URI getBaseUri() { return baseUriThreadLocal.get(); } /** * Remove the current thread's value for baseUri thread-local variable (set by {@link #setBaseUri(java.net.URI)}). * * Should be called after resource method invocation for cleanup. * * @see #location(java.net.URI) * @since 2.4 */ public static void clearBaseUri() { baseUriThreadLocal.remove(); } /** * Create new outbound JAX-RS response builder. * * @param context underlying outbound message context. */ public Builder(final OutboundMessageContext context) { this.context = context; } @Override public jakarta.ws.rs.core.Response build() { StatusType st = status; if (st == null) { st = context.hasEntity() ? Status.OK : Status.NO_CONTENT; } return new OutboundJaxrsResponse(st, new OutboundMessageContext(context)); } @SuppressWarnings({"CloneDoesntCallSuperClone", "CloneDoesntDeclareCloneNotSupportedException"}) @Override public ResponseBuilder clone() { return new Builder(new OutboundMessageContext(context)).status(status); } @Override public jakarta.ws.rs.core.Response.ResponseBuilder status(StatusType status) { if (status == null) { throw new IllegalArgumentException("Response status must not be 'null'"); } this.status = status; return this; } @Override public ResponseBuilder status(int status, final String reasonPhrase) { if (status < 100 || status > 599) { throw new IllegalArgumentException("Response status must not be less than '100' or greater than '599'"); } final Status.Family family = Status.Family.familyOf(status); this.status = new StatusType() { @Override public int getStatusCode() { return status; } @Override public Status.Family getFamily() { return family; } @Override public String getReasonPhrase() { return reasonPhrase; } }; return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder status(int code) { this.status = Statuses.from(code); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder entity(Object entity) { context.setEntity(entity); return this; } @Override public ResponseBuilder entity(Object entity, Annotation[] annotations) { context.setEntity(entity, annotations); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder type(MediaType type) { context.setMediaType(type); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder type(String type) { return type(type == null ? null : MediaType.valueOf(type)); } @Override public jakarta.ws.rs.core.Response.ResponseBuilder variant(Variant variant) { if (variant == null) { type((MediaType) null); language((String) null); encoding(null); return this; } type(variant.getMediaType()); language(variant.getLanguage()); encoding(variant.getEncoding()); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder variants(List variants) { if (variants == null) { header(HttpHeaders.VARY, null); return this; } if (variants.isEmpty()) { return this; } MediaType accept = variants.get(0).getMediaType(); boolean vAccept = false; Locale acceptLanguage = variants.get(0).getLanguage(); boolean vAcceptLanguage = false; String acceptEncoding = variants.get(0).getEncoding(); boolean vAcceptEncoding = false; for (Variant v : variants) { vAccept |= !vAccept && vary(v.getMediaType(), accept); vAcceptLanguage |= !vAcceptLanguage && vary(v.getLanguage(), acceptLanguage); vAcceptEncoding |= !vAcceptEncoding && vary(v.getEncoding(), acceptEncoding); } StringBuilder vary = new StringBuilder(); append(vary, vAccept, HttpHeaders.ACCEPT); append(vary, vAcceptLanguage, HttpHeaders.ACCEPT_LANGUAGE); append(vary, vAcceptEncoding, HttpHeaders.ACCEPT_ENCODING); if (vary.length() > 0) { header(HttpHeaders.VARY, vary.toString()); } return this; } private boolean vary(MediaType v, MediaType vary) { return v != null && !v.equals(vary); } private boolean vary(Locale v, Locale vary) { return v != null && !v.equals(vary); } private boolean vary(String v, String vary) { return v != null && !v.equalsIgnoreCase(vary); } private void append(StringBuilder sb, boolean v, String s) { if (v) { if (sb.length() > 0) { sb.append(','); } sb.append(s); } } @Override public jakarta.ws.rs.core.Response.ResponseBuilder language(String language) { headerSingle(HttpHeaders.CONTENT_LANGUAGE, language); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder language(Locale language) { headerSingle(HttpHeaders.CONTENT_LANGUAGE, language); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder location(URI location) { URI locationUri = location; if (location != null && !location.isAbsolute()) { URI baseUri = getBaseUri(); if (baseUri != null) { locationUri = baseUri.resolve(location); } } headerSingle(HttpHeaders.LOCATION, locationUri); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder contentLocation(URI location) { headerSingle(HttpHeaders.CONTENT_LOCATION, location); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder encoding(String encoding) { headerSingle(HttpHeaders.CONTENT_ENCODING, encoding); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder tag(EntityTag tag) { headerSingle(HttpHeaders.ETAG, tag); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder tag(String tag) { return tag(tag == null ? null : new EntityTag(tag)); } @Override public jakarta.ws.rs.core.Response.ResponseBuilder lastModified(Date lastModified) { headerSingle(HttpHeaders.LAST_MODIFIED, lastModified); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder cacheControl(CacheControl cacheControl) { headerSingle(HttpHeaders.CACHE_CONTROL, cacheControl); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder expires(Date expires) { headerSingle(HttpHeaders.EXPIRES, expires); return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder cookie(NewCookie... cookies) { if (cookies != null) { for (NewCookie cookie : cookies) { header(HttpHeaders.SET_COOKIE, cookie); } } else { header(HttpHeaders.SET_COOKIE, null); } return this; } @Override public jakarta.ws.rs.core.Response.ResponseBuilder header(String name, Object value) { return header(name, value, false); } private jakarta.ws.rs.core.Response.ResponseBuilder headerSingle(String name, Object value) { return header(name, value, true); } private jakarta.ws.rs.core.Response.ResponseBuilder header(String name, Object value, boolean single) { if (value != null) { if (single) { context.getHeaders().putSingle(name, value); } else { context.getHeaders().add(name, value); } } else { context.getHeaders().remove(name); } return this; } @Override public ResponseBuilder variants(Variant... variants) { return variants(Arrays.asList(variants)); } @Override public ResponseBuilder links(Link... links) { if (links != null) { for (Link link : links) { header(HttpHeaders.LINK, link); } } else { header(HttpHeaders.LINK, null); } return this; } @Override public ResponseBuilder link(URI uri, String rel) { header(HttpHeaders.LINK, Link.fromUri(uri).rel(rel).build()); return this; } @Override public ResponseBuilder link(String uri, String rel) { header(HttpHeaders.LINK, Link.fromUri(uri).rel(rel).build()); return this; } @Override public ResponseBuilder allow(String... methods) { if (methods == null || (methods.length == 1 && methods[0] == null)) { return allow((Set) null); } else { return allow(new HashSet(Arrays.asList(methods))); } } @Override public ResponseBuilder allow(Set methods) { if (methods == null) { return header(HttpHeaders.ALLOW, null, true); } StringBuilder allow = new StringBuilder(); for (String m : methods) { append(allow, true, m); } return header(HttpHeaders.ALLOW, allow, true); } @Override public ResponseBuilder replaceAll(MultivaluedMap headers) { context.replaceHeaders(headers); return this; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy