Package org.candlepin.resteasy.interceptor

Source Code of org.candlepin.resteasy.interceptor.LinkHeaderPostInterceptor

/**
* Copyright (c) 2009 - 2012 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package org.candlepin.resteasy.interceptor;

import org.candlepin.common.config.Configuration;
import org.candlepin.config.ConfigProperties;
import org.candlepin.paging.Page;
import org.candlepin.paging.PageRequest;
import org.candlepin.paging.Paginate;

import com.google.inject.Inject;

import org.jboss.resteasy.annotations.interception.Precedence;
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.LinkHeader;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.interception.AcceptedByMethod;
import org.jboss.resteasy.spi.interception.PostProcessInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.ext.Provider;

/**
* LinkHeaderPostProcessor
*/
@Provider
@ServerInterceptor
@Precedence("HEADER_DECORATOR")
public class LinkHeaderPostInterceptor implements PostProcessInterceptor, AcceptedByMethod {
    private static Logger log = LoggerFactory.getLogger(LinkHeaderPostInterceptor.class);
    public static final String LINK_HEADER = "Link";

    private Configuration config;

    @Inject
    public LinkHeaderPostInterceptor(Configuration config) {
        this.config = config;
    }

    @Override
    public boolean accept(Class declaring, Method method) {
        return method.isAnnotationPresent(Paginate.class);
    }

    @Override
    @SuppressWarnings("rawtypes")
    public void postProcess(ServerResponse response) {
        Page page = ResteasyProviderFactory.getContextData(Page.class);

        if (page == null) {
            log.warn("Method marked for pagination, but no page exists in the context.");
            return;
        }

        // If we aren't paging, then no need for Link headers.
        if (page.getPageRequest() == null || !page.getPageRequest().isPaging()) {
            return;
        }

        HttpServletRequest request = ResteasyProviderFactory.getContextData(
            HttpServletRequest.class);
        UriBuilder builder = buildBaseUrl(request);
        // If builder is null, we couldn't read the request URI, so stop.
        if (builder == null) {
            return;
        }

        MultivaluedMap<String, String> params = null;
        try {
            params = extractParameters(request.getQueryString());
        }
        catch (UnsupportedEncodingException e) {
            log.warn("Could not find UTF-8 encoding", e);
            return;
        }

        builder = addUnchangingQueryParams(builder, params);
        //TODO add missing parameters like the default limit if no limit is given.

        LinkHeader header = new LinkHeader();

        Integer next = getNextPage(page);
        if (next != null) {
            header.addLink(null, "next", buildPageLink(builder, next), null);
        }

        Integer prev = getPrevPage(page);
        if (prev != null) {
            header.addLink(null, "prev", buildPageLink(builder, prev), null);
        }

        header.addLink(null, "first", buildPageLink(builder, 1), null);
        header.addLink(null, "last", buildPageLink(builder, getLastPage(page)), null);
        response.getMetadata().add(LINK_HEADER, header.toString());
    }

    protected String buildPageLink(UriBuilder b, int value) {
        // Copy so we can use the same builder for building each link.
        UriBuilder builder = b.clone();
        builder.queryParam(PageRequest.PAGE_PARAM, String.valueOf(value));
        return builder.build().toString();
    }

    protected Integer getLastPage(Page page) {
        PageRequest pageRequest = page.getPageRequest();

        // The last page is ceiling(maxRecords/recordsPerPage)
        int lastPage = page.getMaxRecords() / pageRequest.getPerPage();

        if (page.getMaxRecords() % pageRequest.getPerPage() != 0) {
            lastPage++;
        }

        return lastPage;
    }

    protected Integer getPrevPage(Page page) {
        Integer prev = page.getPageRequest().getPage() - 1;
        // if the calculated page is out of bounds, return null
        return (prev < 1 || prev >= getLastPage(page)) ? null : prev;
    }

    protected Integer getNextPage(Page page) {
        Integer next = page.getPageRequest().getPage() + 1;
        return (next > getLastPage(page)) ? null : next;
    }

    protected UriBuilder buildBaseUrl(HttpServletRequest request) {
        StringBuffer url;
        if (config.containsKey(ConfigProperties.PREFIX_APIURL) &&
            !"".equals(config.getString(ConfigProperties.PREFIX_APIURL))) {
            url = new StringBuffer(config.getString(ConfigProperties.PREFIX_APIURL));
            // The default value of PREFIX_APIURL doesn't specify a scheme.
            if (url.indexOf("://") == -1) {
                url = new StringBuffer("https://").append(url);
            }

            // Now add on the resource they requested
            // We can't just use request.getServletPath().  Something RESTEasy is doing?

            // Context path should equal something like /candlepin
            String context = request.getContextPath();

            // Request URI should equal something like /candlepin/resource
            String requestUri = request.getRequestURI();

            int offset = requestUri.lastIndexOf(context);
            if (offset >= 0) {
                // Strip off the context
                url.append(requestUri.substring(offset + context.length()));
            }
            else {
                // Something has gone really wrong if the context isn't in the
                // request URI.
                log.warn("Could not determine resource path.");
                return null;
            }
        }
        else {
            url = request.getRequestURL();
        }

        try {
            UriBuilder builder = UriBuilder.fromUri(url.toString());
            return builder;
        }
        catch (IllegalArgumentException e) {
            log.warn("Could not build URI from " + url, e);
            return null;
        }
    }

    /**
     * Based on RESTEasy's UriInfoImpl.extractParameters()
     * @param queryString
     * @return
     * @throws UnsupportedEncodingException
     */
    protected MultivaluedMap<String, String> extractParameters(String queryString)
        throws UnsupportedEncodingException {
        if (queryString == null || "".equals(queryString)) {
            return null;
        }

        MultivaluedMap<String, String> map = new MultivaluedMapImpl<String, String>();
        String[] params = queryString.split("&");

        for (String param : params) {
            if (param.indexOf('=') >= 0) {
                String[] nv = param.split("=");
                // Have to decode because UriBuilder re-encodes.
                String name = URLDecoder.decode(nv[0], "UTF-8");
                String val = (nv.length > 1) ? nv[1] : "";
                map.add(name, URLDecoder.decode(val, "UTF-8"));
            }
            else {
                map.add(param, "");
            }
        }

        return map;
    }

    protected UriBuilder addUnchangingQueryParams(UriBuilder builder,
        MultivaluedMap<String, String> params) {
        // This will take care of adding back any order, per_page, or sort_by
        // parameters provided too.
        if (params != null) {
            for (Entry<String, List<String>> e : params.entrySet()) {
                if (!e.getKey().equals(PageRequest.PAGE_PARAM)) {
                    for (String v : e.getValue()) {
                        builder = builder.queryParam(e.getKey(), v);
                    }
                }
            }
        }
        return builder;
    }
}
TOP

Related Classes of org.candlepin.resteasy.interceptor.LinkHeaderPostInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.