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

org.synchronoss.cloud.nio.multipart.BlockingIOAdapter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2015 Synchronoss Technologies
 *
 * Licensed 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.
 */
package org.synchronoss.cloud.nio.multipart;

import org.synchronoss.cloud.nio.multipart.util.collect.AbstractIterator;
import org.synchronoss.cloud.nio.multipart.util.collect.CloseableIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.synchronoss.cloud.nio.stream.storage.StreamStorage;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import static org.synchronoss.cloud.nio.multipart.NioMultipartParser.DEFAULT_BUFFER_SIZE;
import static org.synchronoss.cloud.nio.multipart.NioMultipartParser.DEFAULT_HEADERS_SECTION_SIZE;
import static org.synchronoss.cloud.nio.multipart.NioMultipartParser.DEFAULT_MAX_LEVEL_OF_NESTED_MULTIPART;

/**
 * 

Adapts the {@link NioMultipartParser} to work with blocking IO. * The adapter exposes a set of static methods that return a {@code CloseableIterator} over the parts. *

An alternative way to obtain the {@code CloseableIterator} is using the fluent API offered by {@link Multipart}. * * @author Silvano Riz */ public class BlockingIOAdapter { private static final Logger log = LoggerFactory.getLogger(BlockingIOAdapter.class); /** *

* Parses the multipart stream and it returns the parts in form of {@code CloseableIterator}. *

* @param inputStream The multipart stream * @param multipartContext The multipart context * @return the parts in the form of a closeable iterator */ public static CloseableIterator parse(final InputStream inputStream, final MultipartContext multipartContext){ return parse(inputStream, multipartContext, null, DEFAULT_BUFFER_SIZE, DEFAULT_HEADERS_SECTION_SIZE, DEFAULT_MAX_LEVEL_OF_NESTED_MULTIPART); } /** *

* Parses the multipart stream and it return the parts in form of {@code CloseableIterator}. *

* * @param inputStream The multipart stream * @param multipartContext The multipart context * @param partBodyStreamStorageFactory The {@code PartBodyStreamStorageFactory} to use * @return the parts in the form of a closeable iterator */ public static CloseableIterator parse(final InputStream inputStream, final MultipartContext multipartContext, final PartBodyStreamStorageFactory partBodyStreamStorageFactory) { return parse(inputStream, multipartContext, partBodyStreamStorageFactory, DEFAULT_BUFFER_SIZE, DEFAULT_HEADERS_SECTION_SIZE, DEFAULT_MAX_LEVEL_OF_NESTED_MULTIPART); } /** *

* Parses the multipart stream and it return the parts in form of {@code CloseableIterator}. *

* * @param inputStream The multipart stream * @param multipartContext The multipart context * @param bufferSize The buffer size in bytes * @return the parts in the form of a closeable iterator */ public static CloseableIterator parse(final InputStream inputStream, final MultipartContext multipartContext, final int bufferSize) { return parse(inputStream, multipartContext, null, bufferSize, DEFAULT_HEADERS_SECTION_SIZE, DEFAULT_MAX_LEVEL_OF_NESTED_MULTIPART); } /** *

* Parses the multipart stream and it return the parts in form of {@link Iterable}. *

* * @param inputStream The multipart stream * @param multipartContext The multipart context * @param partBodyStreamStorageFactory The {@code PartBodyStreamStorageFactory} to use * @param bufferSize The buffer size in bytes * @param maxHeadersSectionSize The max size of the headers section in bytes * @param maxLevelOfNestedMultipart the max number of nested multipart * @return the parts in the form of a closeable iterator */ @SuppressWarnings("unchecked") public static CloseableIterator parse(final InputStream inputStream, final MultipartContext multipartContext, final PartBodyStreamStorageFactory partBodyStreamStorageFactory, final int bufferSize, final int maxHeadersSectionSize, final int maxLevelOfNestedMultipart) { return new PartItemsIterator(inputStream, multipartContext, partBodyStreamStorageFactory, bufferSize, maxHeadersSectionSize, maxLevelOfNestedMultipart); } static class PartItemsIterator extends AbstractIterator implements CloseableIterator { private static final ParserToken END_OF_DATA = new ParserToken() { @Override public Type getType() { return null; } }; private Queue parserTokens = new ConcurrentLinkedQueue<>(); private final NioMultipartParser parser; private final InputStream inputStream; public PartItemsIterator(final InputStream inputStream, final MultipartContext multipartContext, final PartBodyStreamStorageFactory partBodyStreamStorageFactory, final int bufferSize, final int maxHeadersSectionSize, final int maxLevelOfNestedMultipart) { this.inputStream = inputStream; final NioMultipartParserListener listener = new NioMultipartParserListener() { @Override public void onPartFinished(StreamStorage partBodyStreamStorage, Map> headersFromPart) { parserTokens.add(new Part(headersFromPart, partBodyStreamStorage)); } @Override public void onAllPartsFinished() { parserTokens.add(END_OF_DATA); } @Override public void onNestedPartStarted(Map> headersFromParentPart) { parserTokens.add(new NestedStart(headersFromParentPart)); } @Override public void onNestedPartFinished() { parserTokens.add(new NestedEnd()); } @Override public void onError(String message, Throwable cause) { throw new IllegalStateException("Error parsing the multipart stream: " + message, cause); } }; this.parser = new NioMultipartParser(multipartContext, listener, partBodyStreamStorageFactory, bufferSize, maxHeadersSectionSize, maxLevelOfNestedMultipart); } @Override protected ParserToken computeNext() { byte[] buffer = new byte[1024]; int read; try { ParserToken next; next = parserTokens.poll(); if (next != null && next.getType() == null){ return endOfData(); } if (next != null){ return next; } while (null == (next = parserTokens.poll()) && -1 != (read = inputStream.read(buffer))) { parser.write(buffer, 0, read); } if (next != null && next.getType() == null){ return endOfData(); } if (next != null){ return next; } throw new IllegalStateException("Error parsing the multipart stream. Stream ended unexpectedly"); }catch (Exception e){ throw new IllegalStateException("Error parsing the multipart stream", e); } } @Override public void close() throws IOException { parser.close(); } } /** *

Interface representing a parser token. *

The parser tokens can be: *

    *
  • Part: it contains the parsed part including headers and body
  • *
  • Nested Start: it is a marker token that indicates the start of a nested part. It give access to the headers of the parent part.
  • *
  • Nested End: it is a marker token that indicates the end of a nested part.
  • *
*

The {@code BlockingIOAdapter} is returning a {@code CloseableIterator} of {@code ParserToken}s. */ public interface ParserToken { /** * Type of a token: part, nested (start) and nested (end) */ enum Type{ PART, NESTED_START, NESTED_END } /** *

Returns the type of the part * * @return The type of the part */ Type getType(); } /** *

Marker {@code PartItem} signalling the end of a nested multipart. The client can keep track of nested multipart * if used in combination with {@code NestedStart}. Otherwise the item can just be skipped during the processing. */ public static class NestedEnd implements ParserToken { private NestedEnd(){} /** * {@inheritDoc} */ @Override public Type getType() { return Type.NESTED_END; } } /** *

Marker {@code PartItem} signalling the start of a nested multipart. In addition to signal the start of a multipart * it provides access to the headers of the parent part. */ public static class NestedStart implements ParserToken { final Map> headers; private NestedStart(final Map> headers) { this.headers = headers; } /** * {@inheritDoc} */ @Override public Type getType() { return Type.NESTED_START; } /** *

Returns the parent part headers. * * @return The part headers. */ public Map> getHeaders() { return headers; } } /** *

A {@code PartItem} representing an attachment part. * It gives access to the part headers and the body {@code InputStream} */ public static class Part implements ParserToken { final Map> headers; final StreamStorage partBodyStreamStorage; private Part(final Map> headers, final StreamStorage partBodyStreamStorage) { this.headers = headers; this.partBodyStreamStorage = partBodyStreamStorage; } /** * {@inheritDoc} */ @Override public Type getType() { return Type.PART; } /** *

Returns the part headers. * * @return The part headers. */ public Map> getHeaders() { return headers; } /** *

Returns the {@code InputStream} from where the part body can be read. * * @return the {@code InputStream} from where the part body can be read. */ public InputStream getPartBody(){ return partBodyStreamStorage.getInputStream(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy