org.apache.abdera.protocol.server.ProviderHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.abdera.protocol.server.ProviderHelper.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  The ASF licenses this file to You
 * under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
package org.apache.abdera.protocol.server;

import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.activation.MimeType;
import javax.xml.namespace.QName;

import org.apache.abdera.Abdera;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.i18n.text.Localizer;
import org.apache.abdera.i18n.text.Sanitizer;
import org.apache.abdera.model.AtomDate;
import org.apache.abdera.model.Base;
import org.apache.abdera.model.Content;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.ExtensibleElement;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Link;
import org.apache.abdera.protocol.error.Error;
import org.apache.abdera.protocol.server.context.AbstractResponseContext;
import org.apache.abdera.protocol.server.context.BaseResponseContext;
import org.apache.abdera.protocol.server.context.EmptyResponseContext;
import org.apache.abdera.protocol.server.context.StreamWriterResponseContext;
import org.apache.abdera.util.Constants;
import org.apache.abdera.util.EntityTag;
import org.apache.abdera.util.MimeTypeHelper;
import org.apache.abdera.writer.NamedWriter;
import org.apache.abdera.writer.StreamWriter;
import org.apache.abdera.writer.WriterFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Provides support methods for {@link Provider}
 */
public class ProviderHelper {
    private final static Log log = LogFactory.getLog(ProviderHelper.class);

    private ProviderHelper() {
    }

    public static int getPageSize(RequestContext request, String pagesizeparam, int defaultpagesize) {
        int size = defaultpagesize;
        try {
            String _ps = request.getParameter(pagesizeparam);
            size = (_ps != null) ? Math.min(Math.max(Integer.parseInt(_ps), 0), defaultpagesize) : defaultpagesize;
        } catch (Exception e) {
        }
        log.debug(Localizer.sprintf("PAGE.SIZE", size));
        return size;
    }

    public static int getOffset(RequestContext request, String pageparam, int pageSize) {
        int offset = 0;
        try {
            String _page = request.getParameter(pageparam);
            int page = (_page != null) ? Integer.parseInt(_page) : 1;
            page = Math.max(page, 1) - 1;
            offset = pageSize * page;
        } catch (Exception e) {
        }
        log.debug(Localizer.sprintf("OFFSET", offset));
        return offset;
    }

    /**
     * Returns an Error document based on the StreamWriter
     */
    public static AbstractResponseContext createErrorResponse(Abdera abdera, final int code, final String message) {
        return createErrorResponse(abdera, code, message, null);
    }

    /**
     * Returns an Error document based on the StreamWriter
     */
    public static AbstractResponseContext createErrorResponse(Abdera abdera, final int code, final String message,
            final Throwable t) {
        AbstractResponseContext rc = new StreamWriterResponseContext(abdera) {
            protected void writeTo(StreamWriter sw) throws IOException {
                Error.create(sw, code, message, t);
            }
        };
        rc.setStatus(code);
        rc.setStatusText(message);
        return rc;
    }

    /**
     * Return a server error
     */
    public static ResponseContext servererror(RequestContext request, String reason, Throwable t) {
        log.info(Localizer.get("SERVER_ERROR"), t);
        return createErrorResponse(request.getAbdera(), 500, reason, t);
    }

    /**
     * Return a server error
     */
    public static ResponseContext servererror(RequestContext request, Throwable t) {
        return servererror(request, "Server Error", t);
    }

    /**
     * Return an unauthorized error
     */
    public static ResponseContext unauthorized(RequestContext request, String reason) {
        log.debug(Localizer.get("UNAUTHORIZED"));
        return createErrorResponse(request.getAbdera(), 401, reason);
    }

    public static ResponseContext unauthorized(RequestContext request) {
        return unauthorized(request, "Unauthorized");
    }

    /**
     * Return an unauthorized error
     */
    public static ResponseContext forbidden(RequestContext request, String reason) {
        log.debug(Localizer.get("FORBIDDEN"));
        return createErrorResponse(request.getAbdera(), 403, reason);
    }

    public static ResponseContext forbidden(RequestContext request) {
        return forbidden(request, "Forbidden");
    }

    /**
     * Return a 204 No Content response
     */
    public static ResponseContext nocontent(String reason) {
        return new EmptyResponseContext(204, reason);
    }

    public static ResponseContext nocontent() {
        return nocontent("Not Content");
    }

    /**
     * Return a 404 not found error
     */
    public static ResponseContext notfound(RequestContext request, String reason) {
        log.debug(Localizer.get("UNKNOWN"));
        return createErrorResponse(request.getAbdera(), 404, reason);
    }

    public static ResponseContext notfound(RequestContext request) {
        return notfound(request, "Not Found");
    }

    /**
     * Return a 405 method not allowed error
     */
    public static ResponseContext notallowed(RequestContext request, String reason, String... methods) {
        log.debug(Localizer.get("NOT.ALLOWED"));
        AbstractResponseContext resp = createErrorResponse(request.getAbdera(), 405, reason);
        resp.setAllow(methods);
        return resp;
    }

    public static ResponseContext notallowed(RequestContext request, String... methods) {
        return notallowed(request, "Method Not Allowed", methods);
    }

    public static ResponseContext notallowed(RequestContext request) {
        return notallowed(request, getDefaultMethods(request));
    }

    /**
     * Return a 400 bad request error
     */
    public static ResponseContext badrequest(RequestContext request, String reason) {
        log.debug(Localizer.get("BAD.REQUEST"));
        return createErrorResponse(request.getAbdera(), 400, reason);
    }

    public static ResponseContext badrequest(RequestContext request) {
        return badrequest(request, "Bad Request");
    }

    /**
     * Return a 409 conflict error
     */
    public static ResponseContext conflict(RequestContext request, String reason) {
        log.debug(Localizer.get("CONFLICT"));
        return createErrorResponse(request.getAbdera(), 409, reason);
    }

    public static ResponseContext conflict(RequestContext request) {
        return conflict(request, "Conflict");
    }

    /**
     * Return a service unavailable error
     */
    public static ResponseContext unavailable(RequestContext request, String reason) {
        log.debug(Localizer.get("UNAVAILABLE"));
        return createErrorResponse(request.getAbdera(), 503, reason);
    }

    public static ResponseContext unavailable(RequestContext request) {
        return unavailable(request, "Service Unavailable");
    }

    public static ResponseContext notmodified(RequestContext request, String reason) {
        log.debug(Localizer.get("NOT.MODIFIED"));
        return new EmptyResponseContext(304, reason);
    }

    public static ResponseContext notmodified(RequestContext request) {
        return notmodified(request, "Not Modified");
    }

    public static ResponseContext preconditionfailed(RequestContext request, String reason) {
        log.debug(Localizer.get("PRECONDITION.FAILED"));
        return createErrorResponse(request.getAbdera(), 412, reason);
    }

    public static ResponseContext preconditionfailed(RequestContext request) {
        return preconditionfailed(request, "Precondition Failed");
    }

    /**
     * Return a 415 media type not-supported error
     */
    public static ResponseContext notsupported(RequestContext request, String reason) {
        log.debug(Localizer.get("NOT.SUPPORTED"));
        return createErrorResponse(request.getAbdera(), 415, reason);
    }

    public static ResponseContext notsupported(RequestContext request) {
        return notsupported(request, "Media Type Not Supported");
    }

    /**
     * Return a 423 locked error
     */
    public static ResponseContext locked(RequestContext request, String reason) {
        log.debug(Localizer.get("LOCKED"));
        return createErrorResponse(request.getAbdera(), 423, reason);
    }

    public static ResponseContext locked(RequestContext request) {
        return locked(request, "Locked");
    }

    /**
     * Return a document
     */
    @SuppressWarnings("unchecked")
    public static ResponseContext returnBase(Base base, int status, Date lastModified) {
        log.debug(Localizer.get("RETURNING.DOCUMENT"));
        BaseResponseContext response = new BaseResponseContext(base);
        response.setStatus(status);
        if (lastModified != null)
            response.setLastModified(lastModified);
        // response.setContentType(MimeTypeHelper.getMimeType(base));
        Document doc = base instanceof Document ? (Document) base : ((Element) base).getDocument();
        if (doc.getEntityTag() != null) {
            response.setEntityTag(doc.getEntityTag());
        } else if (doc.getLastModified() != null) {
            response.setLastModified(doc.getLastModified());
        }
        return response;
    }

    /**
     * Sanitize the value of a Slug header. Any non alphanumeric characters in the slug are replaced with an underscore
     */
    public static String sanitizeSlug(String slug) {
        if (slug == null)
            throw new IllegalArgumentException(Localizer.get("SLUG.NOT.NULL"));
        String sanitized = Sanitizer.sanitize(slug);
        log.debug(Localizer.sprintf("SLUG.SANITIZED", slug, sanitized));
        return sanitized;
    }

    /**
     * Check to see if the entry is minimally valid according to RFC4287. This is not a complete check. It just verifies
     * that the appropriate elements are present and that their values can be accessed.
     */
    public static boolean isValidEntry(Entry entry) {
        try {
            IRI id = entry.getId();
            if (id == null || id.toString().trim().length() == 0 || !id.isAbsolute())
                return false;
            if (entry.getTitle() == null)
                return false;
            if (entry.getUpdated() == null)
                return false;
            if (entry.getAuthor() == null && (entry.getSource() != null && entry.getSource().getAuthor() == null))
                return false;
            Content content = entry.getContentElement();
            if (content == null) {
                if (entry.getAlternateLink() == null)
                    return false;
            } else {
                if ((content.getSrc() != null || content.getContentType() == Content.Type.MEDIA)
                        && entry.getSummaryElement() == null) {
                    log.debug(Localizer.sprintf("CHECKING.VALID.ENTRY", false));
                    return false;
                }
            }
        } catch (Exception e) {
            log.debug(Localizer.sprintf("CHECKING.VALID.ENTRY", false));
            return false;
        }
        log.debug(Localizer.sprintf("CHECKING.VALID.ENTRY", true));
        return true;
    }

    /**
     * Return false if the element contains any extension elements that are not supported
     */
    public static boolean checkElementNamespaces(Element element, List<String> ignore) {
        List<QName> attrs = element.getExtensionAttributes();
        for (QName qname : attrs) {
            String ns = qname.getNamespaceURI();
            if (!ignore.contains(ns))
                return false;
        }
        if (element instanceof ExtensibleElement) {
            ExtensibleElement ext = (ExtensibleElement) element;
            List<Element> extensions = ext.getExtensions();
            for (Element el : extensions) {
                QName qname = el.getQName();
                String ns = qname.getNamespaceURI();
                if (!ignore.contains(ns))
                    return false;
                if (!checkElementNamespaces(el, ignore))
                    return false;
            }
        }
        return true;
    }

    public static boolean beforeOrEqual(Date d1, Date d2) {
        long l1 = d1.getTime() / 1000; // drop milliseconds
        long l2 = d2.getTime() / 1000; // drop milliseconds
        return l1 <= l2;
    }

    public static IRI resolveBase(RequestContext request) {
        return request.getBaseUri().resolve(request.getUri());
    }

    public static String combine(String... vals) {
        StringBuilder buf = new StringBuilder();
        for (String val : vals) {
            if (buf.length() > 0)
                buf.append(", ");
            buf.append(val);
        }
        return buf.toString();
    }

    public static String[] getDefaultMethods(RequestContext request) {
        TargetType type = request.getTarget().getType();
        if (type == null)
            return new String[0];
        if (type == TargetType.TYPE_COLLECTION)
            return new String[] { "GET", "HEAD", "OPTIONS", "POST" };
        if (type == TargetType.TYPE_CATEGORIES)
            return new String[] { "GET", "HEAD", "OPTIONS" };
        if (type == TargetType.TYPE_ENTRY)
            return new String[] { "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT" };
        if (type == TargetType.TYPE_MEDIA)
            return new String[] { "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT" };
        if (type == TargetType.TYPE_SERVICE)
            return new String[] { "GET", "HEAD", "OPTIONS" };
        return new String[] { "GET", "HEAD", "OPTIONS" };
    }

    public static boolean defaultCheckMethod(RequestContext request, String[] methods) {
        return (java.util.Arrays.binarySearch(methods, request.getMethod()) >= 0);
    }

    public static boolean isAtom(RequestContext request) {
        MimeType mt = request.getContentType();
        String ctype = (mt != null) ? mt.toString() : null;
        return ctype != null && MimeTypeHelper.isAtom(ctype);
    }

    private static class QTokenComparator implements Comparator<QToken> {
        public int compare(QToken o1, QToken o2) {
            if (o1.qvalue > o2.qvalue)
                return -1;
            if (o1.qvalue < o2.qvalue)
                return 1;
            return 0;
        }
    }

    private static class QToken {
        String token;
        double qvalue = 1.0;

        QToken(String token, double qvalue) {
            this.token = token;
            this.qvalue = qvalue;
        }
    }

    /**
     * <p>
     * Utility method for parsing HTTP content negotiation headers and sorting their tokens according to their q
     * parameter values.
     * </p>
     * <p>
     * e.g. Accept: audio/*; q=0.2, audio/basic, audio/mpeg; q=0.1
     * </p>
     * <p>
     * would sort into:
     * </p>
     * 
     * <pre>
     *   audio/basic
     *   audio/*
     *   audio/mpeg
     * </pre>
     */
    public static String[] orderByQ(String header) {
        if (header == null || header.length() == 0)
            return new String[0];
        String[] tokens = header.split(",");
        QToken[] qtokens = new QToken[tokens.length];
        for (int i = 0; i < tokens.length; i++) {
            String token = tokens[i];
            String[] qvalues = token.trim().split(";");
            String t = qvalues[0];
            if (qvalues.length > 1) {
                for (int n = 1; n < qvalues.length; n++) {
                    String[] v = qvalues[n].trim().split("=");
                    if (v[0].trim().equals("q")) {
                        double qv = Double.parseDouble(v[1]);
                        qtokens[i] = new QToken(t, qv);
                        break;
                    }
                }
            }
            if (qtokens[i] == null)
                qtokens[i] = new QToken(t, 1.0);
        }
        Arrays.sort(qtokens, new QTokenComparator());
        tokens = new String[qtokens.length];
        for (int n = 0; n < qtokens.length; n++) {
            tokens[n] = qtokens[n].token;
        }
        return tokens;
    }

    /**
     * Returns an appropriate NamedWriter instance given an appropriately formatted HTTP Accept header. The header will
     * be parsed and sorted according to it's q parameter values. The first named writer capable of supporting the
     * specified type, in order of q-value preference, will be returned. The results on this are not always predictable.
     * For instance, if the Accept header says "application/*" it could end up with either the JSON writer or the
     * PrettyXML writer, or any other writer that supports any writer that supports a specific form of "application/*".
     * It's always best to be very specific in the Accept headers.
     */
    public static NamedWriter getAcceptableNamedWriter(Abdera abdera, String accept_header) {
        String[] sorted_accepts = orderByQ(accept_header);
        WriterFactory factory = abdera.getWriterFactory();
        if (factory == null)
            return null;
        for (String accept : sorted_accepts) {
            NamedWriter writer = (NamedWriter) factory.getWriterByMediaType(accept);
            if (writer != null)
                return writer;
        }
        return null;
    }

    public static NamedWriter getNamedWriter(Abdera abdera, String mediatype) {
        WriterFactory factory = abdera.getWriterFactory();
        if (factory == null)
            return null;
        NamedWriter writer = (NamedWriter) factory.getWriterByMediaType(mediatype);
        return writer;
    }

    public static EntityTag calculateEntityTag(Base base) {
        String id = null;
        String modified = null;
        if (base instanceof Entry) {
            Entry entry = (Entry) base;
            id = entry.getId().toString();
            modified = AtomDate.format(entry.getEdited() != null ? entry.getEdited() : entry.getUpdated());
        } else if (base instanceof Feed) {
            Feed feed = (Feed) base;
            id = feed.getId().toString();
            modified = AtomDate.format(feed.getUpdated());
        } else if (base instanceof Document) {
            return calculateEntityTag(((Document<?>) base).getRoot());
        }
        return EntityTag.generate(id, modified);
    }

    public static String getEditUriFromEntry(Entry entry) {
        String editUri = null;
        List<Link> editLinks = entry.getLinks("edit");
        if (editLinks != null) {
            for (Link link : editLinks) {
                // if there is more than one edit link, we should not automatically
                // assume that it's always going to point to an Atom document
                // representation.
                if (link.getMimeType() != null) {
                    if (MimeTypeHelper.isMatch(link.getMimeType().toString(), Constants.ATOM_MEDIA_TYPE)) {
                        editUri = link.getResolvedHref().toString();
                        break;
                    }
                } else {
                    // edit link with no type attribute is the right one to use
                    editUri = link.getResolvedHref().toString();
                    break;
                }
            }
        }
        return editUri;
    }

    public static String[] getAcceptableTypes(RequestContext request) {
        String accept = request.getAccept();
        return orderByQ(accept);
    }

    public static boolean isPreferred(RequestContext request, String s1, String s2) {
        return isPreferred(getAcceptableTypes(request), s1, s2);
    }

    public static boolean isPreferred(String[] accepts, String s1, String s2) {
        int i1 = accepts.length, i2 = accepts.length;
        for (int n = 0; n < accepts.length; n++) {
            if (MimeTypeHelper.isMatch(s1, accepts[n])) {
                i1 = n;
                break;
            }
        }
        for (int n = 0; n < accepts.length; n++) {
            if (MimeTypeHelper.isMatch(s2, accepts[n])) {
                i2 = n;
                break;
            }
        }
        return i1 < i2;
    }
}