001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2005 Emic Networks
004: * Contact: sequoia@continuent.org
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * Initial developer(s): Dylan Hansen.
019: */package org.continuent.sequoia.controller.backup.backupers;
020:
021: import java.io.BufferedReader;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.InputStreamReader;
025: import java.io.OutputStream;
026: import java.util.ArrayList;
027:
028: import org.continuent.sequoia.common.log.Trace;
029:
030: /**
031: * Thread to process output of native commands. This class captures certain
032: * errors and buffers up to a hard-coded number of lines of output. To see all
033: * output turn up log4j debugging to DEBUG level.
034: *
035: * @author <a href="mailto:dhansen@h2st.com">Dylan Hansen</a>
036: * @version 1.0
037: */
038: public class NativeCommandOutputThread extends Thread {
039: // Buffer this many output lines.
040: private static final int MAX_OUTPUT_LINES = 25;
041:
042: ArrayList errors = new ArrayList();
043: ArrayList output = new ArrayList();
044: InputStream inputStream = null;
045: OutputStream outputStream = null;
046:
047: // Logger
048: static Trace logger = Trace
049: .getLogger(NativeCommandOutputThread.class.getName());
050:
051: /**
052: * Creates a new <code>NativeCommandOutputThread</code> object
053: *
054: * @param inStream input stream for command which is closed by Thread when
055: * finished
056: */
057: public NativeCommandOutputThread(InputStream inStream) {
058: inputStream = inStream;
059: }
060:
061: /**
062: * Creates a new <code>NativeCommandOutputThread</code> object that
063: * that transfers all output to the specified output stream.
064: *
065: * @param inStream input stream for command which is closed by thread when
066: * finished
067: * @param outStream Output stream to which output is written; closed by
068: * thread when finished
069: */
070: public NativeCommandOutputThread(InputStream inStream,
071: OutputStream outStream) {
072: inputStream = inStream;
073: outputStream = outStream;
074: }
075:
076: /**
077: * Reads from the input stream and outputs to the log. Adds any error message
078: * to the "errors" ArrayList. Up to MAX_OUTPUT_LINES of output are stored.
079: * IOExceptions are logged and result in thread termination.
080: */
081: public void run() {
082: if (logger.isDebugEnabled())
083: logger.debug("Starting NativeCommandOutputThread: "
084: + this .getName());
085:
086: if (outputStream == null)
087: doNormalOutput();
088: else
089: doRedirectOutput();
090:
091: if (logger.isDebugEnabled())
092: logger.debug("Terminating NativeCommandOutputThread: "
093: + this .toString());
094: }
095:
096: /**
097: * Processes normal output.
098: */
099: protected void doNormalOutput() {
100: BufferedReader buffRead = new BufferedReader(
101: new InputStreamReader(inputStream));
102:
103: String line = null;
104:
105: try {
106: while ((line = buffRead.readLine()) != null) {
107: // Check to see if PostgreSQL output contains "FATAL: " or "ERROR: "
108: if (line.indexOf("FATAL: ") > -1
109: || line.indexOf("ERROR: ") > -1) {
110: errors.add(line);
111: }
112: if (output.size() < MAX_OUTPUT_LINES) {
113: output.add(line);
114: }
115:
116: if (logger.isDebugEnabled())
117: logger.debug(this .getName() + ": " + line);
118: }
119: } catch (IOException ioe) {
120: logger.warn(ioe.getMessage(), ioe);
121: } finally {
122: // Must close stream in this thread to avoid synchronization problems
123: try {
124: buffRead.close();
125: } catch (IOException ioe) {
126: logger.warn(ioe.getMessage(), ioe);
127: }
128: }
129: }
130:
131: /**
132: * Processes output redirection when output directly into another
133: * output stream.
134: */
135: protected void doRedirectOutput() {
136: try {
137: byte[] buff = new byte[1024];
138: int len = 0;
139: while ((len = this .inputStream.read(buff)) > 0) {
140: outputStream.write(buff, 0, len);
141: }
142: } catch (IOException ioe) {
143: logger.warn(ioe.getMessage(), ioe);
144: } finally {
145: // Must close stream in this thread to avoid synchronization problems
146: try {
147: inputStream.close();
148: outputStream.close();
149: } catch (IOException ioe) {
150: logger.warn(ioe.getMessage(), ioe);
151: }
152: }
153: }
154:
155: /**
156: * Return a list of errors
157: *
158: * @return ArrayList of <code>String</code> objects
159: */
160: public ArrayList getErrors() {
161: return errors;
162: }
163:
164: /**
165: * Return buffered output lines.
166: *
167: * @return Output line array.
168: */
169: public ArrayList getOutput() {
170: return output;
171: }
172: }
|