| /** |
| * 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. |
| */ |
| package org.apache.avro.io; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| |
| import org.apache.avro.AvroRuntimeException; |
| import org.apache.avro.Schema; |
| import org.codehaus.jackson.JsonGenerator; |
| |
| /** |
| * A factory for creating and configuring {@link Encoder} instances. |
| * <p/> |
| * Factory methods that create Encoder instances are thread-safe. |
| * Multiple instances with different configurations can be cached |
| * by an application. |
| * |
| * @see Encoder |
| * @see BinaryEncoder |
| * @see JsonEncoder |
| * @see ValidatingEncoder |
| * @see BufferedBinaryEncoder |
| * @see BlockingBinaryEncoder |
| * @see DirectBinaryEncoder |
| */ |
| |
| public class EncoderFactory { |
| private static final int DEFAULT_BUFFER_SIZE = 2048; |
| private static final int DEFAULT_BLOCK_BUFFER_SIZE = 64 * 1024; |
| private static final int MIN_BLOCK_BUFFER_SIZE = 64; |
| private static final int MAX_BLOCK_BUFFER_SIZE = 1024 * 1024 * 1024; |
| |
| private static final EncoderFactory DEFAULT_FACTORY = |
| new DefaultEncoderFactory(); |
| |
| protected int binaryBufferSize = DEFAULT_BUFFER_SIZE; |
| protected int binaryBlockSize = DEFAULT_BLOCK_BUFFER_SIZE; |
| |
| /** |
| * Returns an immutable static DecoderFactory with default configuration. |
| * All configuration methods throw AvroRuntimeExceptions if called. |
| */ |
| public static EncoderFactory get() { |
| return DEFAULT_FACTORY; |
| } |
| |
| /** |
| * Configures this factory to use the specified buffer size when creating |
| * Encoder instances that buffer their output. The default buffer size is 2048 |
| * bytes. |
| * |
| * @param size |
| * The buffer size to configure new instances with. Valid values are |
| * in the range [32, 16*1024*1024]. Values outside this range are set |
| * to the nearest value in the range. Values less than 256 will limit |
| * performance but will consume less memory if the BinaryEncoder is |
| * short-lived, values greater than 8*1024 are not likely to improve |
| * performance but may be useful for the downstream OutputStream. |
| * @return This factory, to enable method chaining: |
| * <pre> |
| * EncoderFactory factory = new EncoderFactory().configureBufferSize(4096); |
| * </pre> |
| * @see #binaryEncoder(OutputStream, BinaryEncoder) |
| */ |
| public EncoderFactory configureBufferSize(int size) { |
| if (size < 32) |
| size = 32; |
| if (size > 16 * 1024 * 1024) |
| size = 16 * 1024 * 1024; |
| this.binaryBufferSize = size; |
| return this; |
| } |
| |
| /** |
| * Returns this factory's configured default buffer size. Used when creating |
| * Encoder instances that buffer writes. |
| * @see #configureBufferSize(int) |
| * @see #binaryEncoder(OutputStream, BinaryEncoder) |
| * @return The preferred buffer size, in bytes. |
| */ |
| public int getBufferSize() { |
| return this.binaryBufferSize; |
| } |
| |
| /** |
| * Configures this factory to construct blocking BinaryEncoders with the |
| * specified block buffer size. The default buffer size is 64 * 1024 bytes. |
| * |
| * @param size |
| * The preferred block size for array blocking. Arrays larger than |
| * this size will be segmented into blocks according to the Avro |
| * spec. Valid values are in the range [64, 1024*1024*1024] Values |
| * outside this range are set to the nearest value in the range. The |
| * encoder will require at least this amount of memory. |
| * @return This factory, to enable method chaining: |
| * <pre> |
| * EncoderFactory factory = new EncoderFactory().configureBlockSize(8000); |
| * </pre> |
| * @see #blockingBinaryEncoder(OutputStream, BinaryEncoder) |
| */ |
| public EncoderFactory configureBlockSize(int size) { |
| if (size < MIN_BLOCK_BUFFER_SIZE) |
| size = MIN_BLOCK_BUFFER_SIZE; |
| if (size > MAX_BLOCK_BUFFER_SIZE) |
| size = MAX_BLOCK_BUFFER_SIZE; |
| this.binaryBlockSize = size; |
| return this; |
| } |
| |
| /** |
| * Returns this factory's configured default block buffer size. |
| * {@link BinaryEncoder} instances created with |
| * #blockingBinaryEncoder(OutputStream, BinaryEncoder) |
| * will have block buffers of this size. |
| * <p/> |
| * @see #configureBlockSize(int) |
| * @see #blockingBinaryEncoder(OutputStream, BinaryEncoder) |
| * @return The preferred block size, in bytes. |
| */ |
| public int getBlockSize() { |
| return this.binaryBlockSize; |
| } |
| |
| /** |
| * Creates or reinitializes a {@link BinaryEncoder} with the OutputStream |
| * provided as the destination for written data. If <i>reuse</i> is provided, |
| * an attempt will be made to reconfigure <i>reuse</i> rather than construct a |
| * new instance, but this is not guaranteed, a new instance may be returned. |
| * <p/> |
| * The {@link BinaryEncoder} implementation returned may buffer its output. |
| * Data may not appear on the underlying OutputStream until |
| * {@link Encoder#flush()} is called. The buffer size is configured with |
| * {@link #configureBufferSize(int)}. |
| * </p> If buffering is not desired, and lower performance is acceptable, use |
| * {@link #directBinaryEncoder(OutputStream, BinaryEncoder)} |
| * <p/> |
| * {@link BinaryEncoder} instances returned by this method are not thread-safe |
| * |
| * @param out |
| * The OutputStream to write to. Cannot be null. |
| * @param reuse |
| * The BinaryEncoder to <i>attempt</i> to reuse given the factory |
| * configuration. A BinaryEncoder implementation may not be |
| * compatible with reuse, causing a new instance to be returned. |
| * If null, a new instance is returned. |
| * @return A BinaryEncoder that uses <i>out</i> as its data output. If |
| * <i>reuse</i> is null, this will be a new instance. If <i>reuse</i> |
| * is not null, then the returned instance may be a new instance or |
| * <i>reuse</i> reconfigured to use <i>out</i>. |
| * @throws IOException |
| * @see BufferedBinaryEncoder |
| * @see Encoder |
| */ |
| public BinaryEncoder binaryEncoder(OutputStream out, BinaryEncoder reuse) { |
| if (null == reuse || !reuse.getClass().equals(BufferedBinaryEncoder.class)) { |
| return new BufferedBinaryEncoder(out, this.binaryBufferSize); |
| } else { |
| return ((BufferedBinaryEncoder)reuse).configure(out, this.binaryBufferSize); |
| } |
| } |
| |
| /** |
| * Creates or reinitializes a {@link BinaryEncoder} with the OutputStream |
| * provided as the destination for written data. If <i>reuse</i> is provided, |
| * an attempt will be made to reconfigure <i>reuse</i> rather than construct a |
| * new instance, but this is not guaranteed, a new instance may be returned. |
| * <p/> |
| * The {@link BinaryEncoder} implementation returned does not buffer its |
| * output, calling {@link Encoder#flush()} will simply cause the wrapped |
| * OutputStream to be flushed. |
| * <p/> |
| * Performance of unbuffered writes can be significantly slower than buffered |
| * writes. {@link #binaryEncoder(OutputStream, BinaryEncoder)} returns |
| * BinaryEncoder instances that are tuned for performance but may buffer output. |
| * The unbuffered, 'direct' encoder may be desired when buffering semantics are |
| * problematic, or if the lifetime of the encoder is so short that the buffer |
| * would not be useful. |
| * <p/> |
| * {@link BinaryEncoder} instances returned by this method are not thread-safe. |
| * |
| * @param out |
| * The OutputStream to initialize to. Cannot be null. |
| * @param reuse |
| * The BinaryEncoder to <i>attempt</i> to reuse given the factory |
| * configuration. A BinaryEncoder implementation may not be |
| * compatible with reuse, causing a new instance to be returned. If |
| * null, a new instance is returned. |
| * @return A BinaryEncoder that uses <i>out</i> as its data output. If |
| * <i>reuse</i> is null, this will be a new instance. If <i>reuse</i> |
| * is not null, then the returned instance may be a new instance or |
| * <i>reuse</i> reconfigured to use <i>out</i>. |
| * @see DirectBinaryEncoder |
| * @see Encoder |
| */ |
| public BinaryEncoder directBinaryEncoder(OutputStream out, BinaryEncoder reuse) { |
| if (null == reuse || !reuse.getClass().equals(DirectBinaryEncoder.class)) { |
| return new DirectBinaryEncoder(out); |
| } else { |
| return ((DirectBinaryEncoder)reuse).configure(out); |
| } |
| } |
| |
| /** |
| * Creates or reinitializes a {@link BinaryEncoder} with the OutputStream |
| * provided as the destination for written data. If <i>reuse</i> is provided, |
| * an attempt will be made to reconfigure <i>reuse</i> rather than construct a |
| * new instance, but this is not guaranteed, a new instance may be returned. |
| * <p/> |
| * The {@link BinaryEncoder} implementation returned buffers its output, |
| * calling {@link Encoder#flush()} is required for output to appear on the underlying |
| * OutputStream. |
| * <p/> |
| * The returned BinaryEncoder implements the Avro binary encoding using blocks |
| * delimited with byte sizes for Arrays and Maps. This allows for some decoders |
| * to skip over large Arrays or Maps without decoding the contents, but adds |
| * some overhead. The default block size is configured with |
| * {@link #configureBlockSize(int)} |
| * <p/> |
| * {@link BinaryEncoder} instances returned by this method are not thread-safe. |
| * |
| * @param out |
| * The OutputStream to initialize to. Cannot be null. |
| * @param reuse |
| * The BinaryEncoder to <i>attempt</i> to reuse given the factory |
| * configuration. A BinaryEncoder implementation may not be |
| * compatible with reuse, causing a new instance to be returned. If |
| * null, a new instance is returned. |
| * @return A BinaryEncoder that uses <i>out</i> as its data output. If |
| * <i>reuse</i> is null, this will be a new instance. If <i>reuse</i> |
| * is not null, then the returned instance may be a new instance or |
| * <i>reuse</i> reconfigured to use <i>out</i>. |
| * @throws IOException |
| * @see BlockingBinaryEncoder |
| * @see Encoder |
| */ |
| public BinaryEncoder blockingBinaryEncoder(OutputStream out, |
| BinaryEncoder reuse) { |
| int blockSize = this.binaryBlockSize; |
| int bufferSize = (blockSize * 2 >= this.binaryBufferSize) ? 32 |
| : this.binaryBufferSize; |
| if (null == reuse || !reuse.getClass().equals(BlockingBinaryEncoder.class)) { |
| return new BlockingBinaryEncoder(out, blockSize, bufferSize); |
| } else { |
| return ((BlockingBinaryEncoder) reuse).configure(out, blockSize, bufferSize); |
| } |
| } |
| |
| /** |
| * Creates a {@link JsonEncoder} using the OutputStream provided for writing |
| * data conforming to the Schema provided. |
| * <p/> |
| * {@link JsonEncoder} buffers its output. Data may not appear on the |
| * underlying OutputStream until {@link Encoder#flush()} is called. |
| * <p/> |
| * {@link JsonEncoder} is not thread-safe. |
| * |
| * @param schema |
| * The Schema for data written to this JsonEncoder. Cannot be null. |
| * @param out |
| * The OutputStream to write to. Cannot be null. |
| * @return A JsonEncoder configured with <i>out</i> and <i>schema</i> |
| * @throws IOException |
| */ |
| public JsonEncoder jsonEncoder(Schema schema, OutputStream out) |
| throws IOException { |
| return new JsonEncoder(schema, out); |
| } |
| |
| /** |
| * Creates a {@link JsonEncoder} using the OutputStream provided for writing |
| * data conforming to the Schema provided with optional pretty printing. |
| * <p/> |
| * {@link JsonEncoder} buffers its output. Data may not appear on the |
| * underlying OutputStream until {@link Encoder#flush()} is called. |
| * <p/> |
| * {@link JsonEncoder} is not thread-safe. |
| * |
| * @param schema |
| * The Schema for data written to this JsonEncoder. Cannot be null. |
| * @param out |
| * The OutputStream to write to. Cannot be null. |
| * @param pretty |
| * Pretty print encoding. |
| * @return A JsonEncoder configured with <i>out</i>, <i>schema</i> and <i>pretty</i> |
| * @throws IOException |
| */ |
| public JsonEncoder jsonEncoder(Schema schema, OutputStream out, boolean pretty) |
| throws IOException { |
| return new JsonEncoder(schema, out, pretty); |
| } |
| |
| /** |
| * Creates a {@link JsonEncoder} using the {@link JsonGenerator} provided for |
| * output of data conforming to the Schema provided. |
| * <p/> |
| * {@link JsonEncoder} buffers its output. Data may not appear on the |
| * underlying output until {@link Encoder#flush()} is called. |
| * <p/> |
| * {@link JsonEncoder} is not thread-safe. |
| * |
| * @param schema |
| * The Schema for data written to this JsonEncoder. Cannot be null. |
| * @param gen |
| * The JsonGenerator to write with. Cannot be null. |
| * @return A JsonEncoder configured with <i>gen</i> and <i>schema</i> |
| * @throws IOException |
| * @deprecated internal method |
| */ |
| @Deprecated |
| public JsonEncoder jsonEncoder(Schema schema, JsonGenerator gen) |
| throws IOException { |
| return new JsonEncoder(schema, gen); |
| } |
| |
| /** |
| * Creates a {@link ValidatingEncoder} that wraps the Encoder provided. |
| * This ValidatingEncoder will ensure that operations against it conform |
| * to the schema provided. |
| * <p/> |
| * Many {@link Encoder}s buffer their output. Data may not appear on the |
| * underlying output until {@link Encoder#flush()} is called. |
| * <p/> |
| * {@link ValidatingEncoder} is not thread-safe. |
| * |
| * @param schema |
| * The Schema to validate operations against. Cannot be null. |
| * @param encoder |
| * The Encoder to wrap. Cannot be be null. |
| * @return A ValidatingEncoder configured to wrap <i>encoder</i> and validate |
| * against <i>schema</i> |
| * @throws IOException |
| */ |
| public ValidatingEncoder validatingEncoder(Schema schema, Encoder encoder) |
| throws IOException { |
| return new ValidatingEncoder(schema, encoder); |
| } |
| |
| // default encoder is not mutable |
| private static class DefaultEncoderFactory extends EncoderFactory { |
| @Override |
| public EncoderFactory configureBlockSize(int size) { |
| throw new AvroRuntimeException("Default EncoderFactory cannot be configured"); |
| } |
| @Override |
| public EncoderFactory configureBufferSize(int size) { |
| throw new AvroRuntimeException("Default EncoderFactory cannot be configured"); |
| } |
| } |
| } |