Java tutorial
/* * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Amazon Software License (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/asl/ * * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.kinesis.multilang; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.concurrent.Callable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This abstract class captures the process of reading from an input stream. Three methods must be provided for * implementations to work. * <ol> * <li> {@link #handleLine(String)}</li> * <li> {@link #returnAfterEndOfInput()}</li> * <li> {@link #returnAfterException(Exception)}</li> * </ol> * * @param <T> */ abstract class LineReaderTask<T> implements Callable<T> { private static final Log LOG = LogFactory.getLog(LineReaderTask.class); private BufferedReader reader; private String description; private String shardId; LineReaderTask() { } /** * Reads lines off the input stream until a return value is set, or an exception is encountered, or the end of the * input stream is reached. Will call the appropriate methods in each case. This is the shared piece of logic * between any tasks that need to read from a child process's STDOUT or STDERR. */ @Override public T call() throws Exception { String nextLine = null; try { LOG.info("Starting: " + description); while ((nextLine = reader.readLine()) != null) { HandleLineResult<T> result = handleLine(nextLine); if (result.hasReturnValue()) { return result.returnValue(); } } } catch (IOException e) { return returnAfterException(e); } LOG.info("Stopping: " + description); return returnAfterEndOfInput(); } /** * Handle a line read from the input stream. The return value indicates whether the enclosing read-loop should * return from the {@link #call()} function by having a value, indicating that value should be returned immediately * without reading further, or not having a value, indicating that more lines of input need to be read before * returning. * * @param line A line read from the input stream. * @return HandleLineResult<T> which may or may not have a has return value, indicating to return or not return yet * respectively. */ protected abstract HandleLineResult<T> handleLine(String line); /** * This method will be called if there is an error while reading from the input stream. The return value of this * method will be returned as the result of this Callable unless an Exception is thrown. If an Exception is thrown * then that exception will be thrown by the Callable. * * @param e An exception that occurred while reading from the input stream. * @return What to return. */ protected abstract T returnAfterException(Exception e) throws Exception; /** * This method will be called once the end of the input stream is reached. The return value of this method will be * returned as the result of this Callable. Implementations of this method are welcome to throw a runtime exception * to indicate that the task was unsuccessful. * * @return What to return. */ protected abstract T returnAfterEndOfInput(); /** * Allows subclasses to provide more detailed logs. Specifically, this allows the drain tasks and GetNextMessageTask * to log which shard they're working on. * * @return The shard id */ public String getShardId() { return this.shardId; } /** * The description should be a string explaining what this particular LineReader class does. * * @return The description. */ public String getDescription() { return this.description; } /** * The result of a call to {@link LineReaderTask#handleLine(String)}. Allows implementations of that method to * indicate whether a particular invocation of that method produced a return for this task or not. If a return value * doesn't exist the {@link #call()} method will continue to the next line. * * @param <V> */ protected class HandleLineResult<V> { private boolean hasReturnValue; private V returnValue; HandleLineResult() { this.hasReturnValue = false; } HandleLineResult(V returnValue) { this.hasReturnValue = true; this.returnValue = returnValue; } boolean hasReturnValue() { return this.hasReturnValue; } V returnValue() { if (hasReturnValue()) { return this.returnValue; } else { throw new RuntimeException("There was no value to return."); } } } /** * An initialization method allows us to delay setting the attributes of this class. Some of the attributes, stream * and shardId, are not known to the {@link MultiLangRecordProcessorFactory} when it constructs a * {@link MultiLangRecordProcessor} but are later determined when * {@link MultiLangRecordProcessor#initialize(String)} is called. So we follow a pattern where the attributes are * set inside this method instead of the constructor so that this object will be initialized when all its attributes * are known to the record processor. * * @param stream * @param shardId * @param description * @return */ protected LineReaderTask<T> initialize(InputStream stream, String shardId, String description) { return this.initialize(new BufferedReader(new InputStreamReader(stream)), shardId, description); } /** * @param reader * @param shardId * @param description * @return */ protected LineReaderTask<T> initialize(BufferedReader reader, String shardId, String description) { this.reader = reader; this.shardId = shardId; this.description = description; return this; } }