You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2013/06/17 12:00:22 UTC

svn commit: r1493700 - in /manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr: ModifiedHttpMultipart.java ModifiedMultipartEntity.java

Author: kwright
Date: Mon Jun 17 10:00:22 2013
New Revision: 1493700

URL: http://svn.apache.org/r1493700
Log:
Port classes I will need to modify from HttpClient; part of CONNECTORS-623.

Added:
    manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedHttpMultipart.java   (with props)
    manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedMultipartEntity.java   (with props)

Added: manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedHttpMultipart.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedHttpMultipart.java?rev=1493700&view=auto
==============================================================================
--- manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedHttpMultipart.java (added)
+++ manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedHttpMultipart.java Mon Jun 17 10:00:22 2013
@@ -0,0 +1,267 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.manifoldcf.agents.output.solr;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.entity.mime.MinimalField;
+import org.apache.http.entity.mime.FormBodyPart;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MIME;
+import org.apache.http.entity.mime.Header;
+import org.apache.http.util.ByteArrayBuffer;
+
+/**
+ * HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is
+ * capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or
+ * the browser compatible modes.
+ *
+ * @since 4.0
+ */
+public class ModifiedHttpMultipart {
+
+    private static ByteArrayBuffer encode(
+            final Charset charset, final String string) {
+        ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
+        ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
+        bab.append(encoded.array(), encoded.position(), encoded.remaining());
+        return bab;
+    }
+
+    private static void writeBytes(
+            final ByteArrayBuffer b, final OutputStream out) throws IOException {
+        out.write(b.buffer(), 0, b.length());
+    }
+
+    private static void writeBytes(
+            final String s, final Charset charset, final OutputStream out) throws IOException {
+        ByteArrayBuffer b = encode(charset, s);
+        writeBytes(b, out);
+    }
+
+    private static void writeBytes(
+            final String s, final OutputStream out) throws IOException {
+        ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
+        writeBytes(b, out);
+    }
+
+    private static void writeField(
+            final MinimalField field, final OutputStream out) throws IOException {
+        writeBytes(field.getName(), out);
+        writeBytes(FIELD_SEP, out);
+        writeBytes(field.getBody(), out);
+        writeBytes(CR_LF, out);
+    }
+
+    private static void writeField(
+            final MinimalField field, final Charset charset, final OutputStream out) throws IOException {
+        writeBytes(field.getName(), charset, out);
+        writeBytes(FIELD_SEP, out);
+        writeBytes(field.getBody(), charset, out);
+        writeBytes(CR_LF, out);
+    }
+
+    private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": ");
+    private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
+    private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
+
+
+    private final String subType;
+    private final Charset charset;
+    private final String boundary;
+    private final List<FormBodyPart> parts;
+
+    private final HttpMultipartMode mode;
+
+    /**
+     * Creates an instance with the specified settings.
+     *
+     * @param subType mime subtype - must not be {@code null}
+     * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
+     * @param boundary to use  - must not be {@code null}
+     * @param mode the mode to use
+     * @throws IllegalArgumentException if charset is null or boundary is null
+     */
+    public ModifiedHttpMultipart(final String subType, final Charset charset, final String boundary, HttpMultipartMode mode) {
+        super();
+        if (subType == null) {
+            throw new IllegalArgumentException("Multipart subtype may not be null");
+        }
+        if (boundary == null) {
+            throw new IllegalArgumentException("Multipart boundary may not be null");
+        }
+        this.subType = subType;
+        this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
+        this.boundary = boundary;
+        this.parts = new ArrayList<FormBodyPart>();
+        this.mode = mode;
+    }
+
+    /**
+     * Creates an instance with the specified settings.
+     * Mode is set to {@link HttpMultipartMode#STRICT}
+     *
+     * @param subType mime subtype - must not be {@code null}
+     * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
+     * @param boundary to use  - must not be {@code null}
+     * @throws IllegalArgumentException if charset is null or boundary is null
+     */
+    public ModifiedHttpMultipart(final String subType, final Charset charset, final String boundary) {
+        this(subType, charset, boundary, HttpMultipartMode.STRICT);
+    }
+
+    public ModifiedHttpMultipart(final String subType, final String boundary) {
+        this(subType, null, boundary);
+    }
+
+    public String getSubType() {
+        return this.subType;
+    }
+
+    public Charset getCharset() {
+        return this.charset;
+    }
+
+    public HttpMultipartMode getMode() {
+        return this.mode;
+    }
+
+    public List<FormBodyPart> getBodyParts() {
+        return this.parts;
+    }
+
+    public void addBodyPart(final FormBodyPart part) {
+        if (part == null) {
+            return;
+        }
+        this.parts.add(part);
+    }
+
+    public String getBoundary() {
+        return this.boundary;
+    }
+
+    private void doWriteTo(
+        final HttpMultipartMode mode,
+        final OutputStream out,
+        boolean writeContent) throws IOException {
+
+        ByteArrayBuffer boundary = encode(this.charset, getBoundary());
+        for (FormBodyPart part: this.parts) {
+            writeBytes(TWO_DASHES, out);
+            writeBytes(boundary, out);
+            writeBytes(CR_LF, out);
+
+            Header header = part.getHeader();
+
+            switch (mode) {
+            case STRICT:
+                for (MinimalField field: header) {
+                    writeField(field, out);
+                }
+                break;
+            case BROWSER_COMPATIBLE:
+                // Only write Content-Disposition
+                // Use content charset
+                MinimalField cd = part.getHeader().getField(MIME.CONTENT_DISPOSITION);
+                writeField(cd, this.charset, out);
+                String filename = part.getBody().getFilename();
+                if (filename != null) {
+                    MinimalField ct = part.getHeader().getField(MIME.CONTENT_TYPE);
+                    writeField(ct, this.charset, out);
+                }
+                break;
+            }
+            writeBytes(CR_LF, out);
+
+            if (writeContent) {
+                part.getBody().writeTo(out);
+            }
+            writeBytes(CR_LF, out);
+        }
+        writeBytes(TWO_DASHES, out);
+        writeBytes(boundary, out);
+        writeBytes(TWO_DASHES, out);
+        writeBytes(CR_LF, out);
+    }
+
+    /**
+     * Writes out the content in the multipart/form encoding. This method
+     * produces slightly different formatting depending on its compatibility
+     * mode.
+     *
+     * @see #getMode()
+     */
+    public void writeTo(final OutputStream out) throws IOException {
+        doWriteTo(this.mode, out, true);
+    }
+
+    /**
+     * Determines the total length of the multipart content (content length of
+     * individual parts plus that of extra elements required to delimit the parts
+     * from one another). If any of the @{link BodyPart}s contained in this object
+     * is of a streaming entity of unknown length the total length is also unknown.
+     * <p/>
+     * This method buffers only a small amount of data in order to determine the
+     * total length of the entire entity. The content of individual parts is not
+     * buffered.
+     *
+     * @return total length of the multipart entity if known, <code>-1</code>
+     *   otherwise.
+     */
+    public long getTotalLength() {
+        long contentLen = 0;
+        for (FormBodyPart part: this.parts) {
+            ContentBody body = part.getBody();
+            long len = body.getContentLength();
+            if (len >= 0) {
+                contentLen += len;
+            } else {
+                return -1;
+            }
+        }
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try {
+            doWriteTo(this.mode, out, false);
+            byte[] extra = out.toByteArray();
+            return contentLen + extra.length;
+        } catch (IOException ex) {
+            // Should never happen
+            return -1;
+        }
+    }
+
+}

Propchange: manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedHttpMultipart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedHttpMultipart.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedMultipartEntity.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedMultipartEntity.java?rev=1493700&view=auto
==============================================================================
--- manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedMultipartEntity.java (added)
+++ manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedMultipartEntity.java Mon Jun 17 10:00:22 2013
@@ -0,0 +1,189 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.manifoldcf.agents.output.solr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.Random;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.entity.mime.FormBodyPart;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MIME;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Multipart/form coded HTTP entity consisting of multiple body parts.
+ *
+ * @since 4.0
+ */
+public class ModifiedMultipartEntity implements HttpEntity {
+
+    /**
+     * The pool of ASCII chars to be used for generating a multipart boundary.
+     */
+    private final static char[] MULTIPART_CHARS =
+        "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            .toCharArray();
+
+    private final ModifiedHttpMultipart multipart;
+    private final Header contentType;
+
+    // @GuardedBy("dirty") // we always read dirty before accessing length
+    private long length;
+    private volatile boolean dirty; // used to decide whether to recalculate length
+
+    /**
+     * Creates an instance using the specified parameters
+     * @param mode the mode to use, may be {@code null}, in which case {@link HttpMultipartMode#STRICT} is used
+     * @param boundary the boundary string, may be {@code null}, in which case {@link #generateBoundary()} is invoked to create the string
+     * @param charset the character set to use, may be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
+     */
+    public ModifiedMultipartEntity(
+            HttpMultipartMode mode,
+            String boundary,
+            Charset charset) {
+        super();
+        if (boundary == null) {
+            boundary = generateBoundary();
+        }
+        if (mode == null) {
+            mode = HttpMultipartMode.STRICT;
+        }
+        this.multipart = new ModifiedHttpMultipart("form-data", charset, boundary, mode);
+        this.contentType = new BasicHeader(
+                HTTP.CONTENT_TYPE,
+                generateContentType(boundary, charset));
+        this.dirty = true;
+    }
+
+    /**
+     * Creates an instance using the specified {@link HttpMultipartMode} mode.
+     * Boundary and charset are set to {@code null}.
+     * @param mode the desired mode
+     */
+    public ModifiedMultipartEntity(final HttpMultipartMode mode) {
+        this(mode, null, null);
+    }
+
+    /**
+     * Creates an instance using mode {@link HttpMultipartMode#STRICT}
+     */
+    public ModifiedMultipartEntity() {
+        this(HttpMultipartMode.STRICT, null, null);
+    }
+
+    protected String generateContentType(
+            final String boundary,
+            final Charset charset) {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("multipart/form-data; boundary=");
+        buffer.append(boundary);
+        if (charset != null) {
+            buffer.append("; charset=");
+            buffer.append(charset.name());
+        }
+        return buffer.toString();
+    }
+
+    protected String generateBoundary() {
+        StringBuilder buffer = new StringBuilder();
+        Random rand = new Random();
+        int count = rand.nextInt(11) + 30; // a random size from 30 to 40
+        for (int i = 0; i < count; i++) {
+            buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
+        }
+        return buffer.toString();
+    }
+
+    public void addPart(final FormBodyPart bodyPart) {
+        this.multipart.addBodyPart(bodyPart);
+        this.dirty = true;
+    }
+
+    public void addPart(final String name, final ContentBody contentBody) {
+        addPart(new FormBodyPart(name, contentBody));
+    }
+
+    public boolean isRepeatable() {
+        for (FormBodyPart part: this.multipart.getBodyParts()) {
+            ContentBody body = part.getBody();
+            if (body.getContentLength() < 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean isChunked() {
+        return !isRepeatable();
+    }
+
+    public boolean isStreaming() {
+        return !isRepeatable();
+    }
+
+    public long getContentLength() {
+        if (this.dirty) {
+            this.length = this.multipart.getTotalLength();
+            this.dirty = false;
+        }
+        return this.length;
+    }
+
+    public Header getContentType() {
+        return this.contentType;
+    }
+
+    public Header getContentEncoding() {
+        return null;
+    }
+
+    public void consumeContent()
+        throws IOException, UnsupportedOperationException{
+        if (isStreaming()) {
+            throw new UnsupportedOperationException(
+                    "Streaming entity does not implement #consumeContent()");
+        }
+    }
+
+    public InputStream getContent() throws IOException, UnsupportedOperationException {
+        throw new UnsupportedOperationException(
+                    "Multipart form entity does not implement #getContent()");
+    }
+
+    public void writeTo(final OutputStream outstream) throws IOException {
+        this.multipart.writeTo(outstream);
+    }
+
+}

Propchange: manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedMultipartEntity.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: manifoldcf/trunk/connectors/solr/connector/src/main/java/org/apache/manifoldcf/agents/output/solr/ModifiedMultipartEntity.java
------------------------------------------------------------------------------
    svn:keywords = Id