com.trsst.server.AbstractMultipartAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.trsst.server.AbstractMultipartAdapter.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 com.trsst.server;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders;

import org.apache.abdera.model.Document;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Source;
import org.apache.abdera.parser.ParseException;
import org.apache.abdera.parser.Parser;
import org.apache.abdera.protocol.server.RequestContext;
import org.apache.abdera.protocol.server.impl.AbstractCollectionAdapter;
import org.apache.abdera.protocol.server.multipart.MultipartInputStream;
import org.apache.abdera.protocol.server.multipart.MultipartRelatedCollectionInfo;
import org.apache.abdera.util.Constants;
import org.apache.abdera.util.MimeTypeHelper;
import org.apache.commons.codec.binary.Base64;

@SuppressWarnings("unchecked")
public abstract class AbstractMultipartAdapter extends AbstractCollectionAdapter
        implements MultipartRelatedCollectionInfo {

    private static final String CONTENT_TYPE_HEADER = "content-type";
    private static final String CONTENT_ID_HEADER = "content-id";
    private static final String START_PARAM = "start";
    private static final String TYPE_PARAM = "type";
    private static final String BOUNDARY_PARAM = "boundary";

    protected Map<String, String> accepts;

    public String[] getAccepts(RequestContext request) {
        Collection<String> acceptKeys = getAlternateAccepts(request).keySet();
        return acceptKeys.toArray(new String[acceptKeys.size()]);
    }

    protected List<MultipartRelatedPost> getMultipartRelatedData(RequestContext request, InputStream requestData)
            throws IOException, ParseException, MessagingException {

        // NOTE: because request.getInputStream() has already been read
        // we pass in a seperate input stream containing the request data
        MultipartInputStream stream = getMultipartStream(request, requestData);
        List<MultipartRelatedPost> result = new LinkedList<MultipartRelatedPost>();
        stream.skipBoundary();

        String start = request.getContentType().getParameter(START_PARAM);

        Document<Source> source = null;
        Map<String, String> entryHeaders = new HashMap<String, String>();
        InputStream data = null;
        Map<String, String> dataHeaders = new HashMap<String, String>();

        Map<String, String> headers = getHeaders(stream);

        // first part is required to be the feed or entry
        if (start == null || start.length() == 0
                || (headers.containsKey(CONTENT_ID_HEADER) && start.equals(headers.get(CONTENT_ID_HEADER)))
                || (headers.containsKey(CONTENT_TYPE_HEADER)
                        && MimeTypeHelper.isAtom(headers.get(CONTENT_TYPE_HEADER)))) {
            source = getEntry(stream, request);
            entryHeaders.putAll(headers);
        } else {
            throw new ParseException("First part was not a feed or entry: " + headers);
            // data = getDataInputStream(multipart);
            // dataHeaders.putAll(headers);
        }

        try {
            while (stream.available() > 0) {
                stream.skipBoundary();
                headers = getHeaders(stream);
                if (start != null
                        && (headers.containsKey(CONTENT_ID_HEADER) && start.equals(headers.get(CONTENT_ID_HEADER)))
                        && (headers.containsKey(CONTENT_TYPE_HEADER)
                                && MimeTypeHelper.isAtom(headers.get(CONTENT_TYPE_HEADER)))) {
                    throw new ParseException("Should not have found a second feed or entry: " + headers);
                } else {
                    data = getDataInputStream(stream);
                    dataHeaders.putAll(headers);
                }
                checkMultipartContent(source, dataHeaders, request);
                result.add(new MultipartRelatedPost(source, data, entryHeaders, dataHeaders));
            }
        } catch (IOException ioe) {
            log.error("Unexpected error parsing multipart data", ioe);
        }
        return result;
    }

    private MultipartInputStream getMultipartStream(RequestContext request, InputStream inputStream)
            throws IOException, ParseException, IllegalArgumentException {
        String boundary = request.getContentType().getParameter(BOUNDARY_PARAM);

        if (boundary == null) {
            throw new IllegalArgumentException("multipart/related stream invalid, boundary parameter is missing.");
        }

        boundary = "--" + boundary;

        String type = request.getContentType().getParameter(TYPE_PARAM);
        if (!(type != null && MimeTypeHelper.isAtom(type))) {
            throw new ParseException(
                    "multipart/related stream invalid, type parameter should be " + Constants.ATOM_MEDIA_TYPE);
        }

        PushbackInputStream pushBackInput = new PushbackInputStream(inputStream, 2);
        pushBackInput.unread("\r\n".getBytes());

        return new MultipartInputStream(pushBackInput, boundary.getBytes());
    }

    private void checkMultipartContent(Document<Source> entry, Map<String, String> dataHeaders,
            RequestContext request) throws ParseException {
        if (entry == null) {
            throw new ParseException("multipart/related stream invalid, media link entry is missing");
        }
        if (!dataHeaders.containsKey(CONTENT_TYPE_HEADER)) {
            throw new ParseException("multipart/related stream invalid, data content-type is missing");
        }
        if (!isContentTypeAccepted(dataHeaders.get(CONTENT_TYPE_HEADER), request)) {
            throw new ParseException("multipart/related stream invalid, content-type "
                    + dataHeaders.get(CONTENT_TYPE_HEADER) + " not accepted into this multipart file");
        }
    }

    private Map<String, String> getHeaders(MultipartInputStream multipart) throws IOException, MessagingException {
        Map<String, String> mapHeaders = new HashMap<String, String>();
        moveToHeaders(multipart);
        InternetHeaders headers = new InternetHeaders(multipart);

        Enumeration<Header> allHeaders = headers.getAllHeaders();
        if (allHeaders != null) {
            while (allHeaders.hasMoreElements()) {
                Header header = allHeaders.nextElement();
                mapHeaders.put(header.getName().toLowerCase(), header.getValue());
            }
        }

        return mapHeaders;
    }

    private boolean moveToHeaders(InputStream stream) throws IOException {
        boolean dash = false;
        boolean cr = false;
        int byteReaded;

        while ((byteReaded = stream.read()) != -1) {
            switch (byteReaded) {
            case '\r':
                cr = true;
                dash = false;
                break;
            case '\n':
                if (cr == true)
                    return true;
                dash = false;
                break;
            case '-':
                if (dash == true) { // two dashes
                    stream.close();
                    return false;
                }
                dash = true;
                cr = false;
                break;
            default:
                dash = false;
                cr = false;
            }
        }
        return false;
    }

    private InputStream getDataInputStream(InputStream stream) throws IOException {
        Base64 base64 = new Base64();
        ByteArrayOutputStream bo = new ByteArrayOutputStream();

        byte[] buffer = new byte[1024];
        int i;
        while ((i = stream.read(buffer)) != -1) {
            bo.write(buffer, 0, i);
        }
        return new ByteArrayInputStream(base64.decode(bo.toByteArray()));
    }

    private <T extends Element> Document<T> getEntry(InputStream stream, RequestContext request)
            throws ParseException, IOException {
        Parser parser = request.getAbdera().getParser();
        if (parser == null)
            throw new IllegalArgumentException("No Parser implementation was provided");
        Document<?> document = parser.parse(stream, request.getResolvedUri().toString(),
                parser.getDefaultParserOptions());
        return (Document<T>) document;
    }

    private boolean isContentTypeAccepted(String contentType, RequestContext request) {
        if (getAlternateAccepts(request) == null) {
            return false;
        }
        for (Map.Entry<String, String> accept : getAlternateAccepts(request).entrySet()) {
            if (accept.getKey().equalsIgnoreCase(contentType) && accept.getValue() != null
                    && accept.getValue().equalsIgnoreCase(Constants.LN_ALTERNATE_MULTIPART_RELATED)) {
                return true;
            }
        }
        return false;
    }

    protected class MultipartRelatedPost {
        private final Document<Source> source;
        private final InputStream data;
        private final Map<String, String> entryHeaders;
        private final Map<String, String> dataHeaders;

        public MultipartRelatedPost(Document<Source> base, InputStream data, Map<String, String> entryHeaders,
                Map<String, String> dataHeaders) {
            this.source = base;
            this.data = data;
            this.entryHeaders = entryHeaders;
            this.dataHeaders = dataHeaders;
        }

        public Document<Source> getSource() {
            return source;
        }

        public InputStream getData() {
            return data;
        }

        public Map<String, String> getEntryHeaders() {
            return entryHeaders;
        }

        public Map<String, String> getDataHeaders() {
            return dataHeaders;
        }
    }

    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass());

}