001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util.channel.impl; 017 018import java.io.IOException; 019 020import org.codehaus.plexus.util.cli.StreamFeeder; 021import org.kuali.common.util.Assert; 022import org.kuali.common.util.channel.model.CommandContext; 023import org.kuali.common.util.stream.StreamPumper; 024 025import com.google.common.base.Optional; 026import com.jcraft.jsch.ChannelExec; 027 028public final class StreamHandler { 029 030 public StreamHandler(CommandContext context) { 031 Assert.noNulls(context); 032 this.context = context; 033 } 034 035 private final CommandContext context; 036 037 private Optional<StreamFeeder> inputFeeder; 038 private StreamPumper outputPumper; 039 private StreamPumper errorPumper; 040 041 private boolean open = false; 042 private boolean pumping = false; 043 private boolean done = false; 044 045 public void openStreams(ChannelExec exec, String encoding) throws IOException { 046 Assert.isFalse(open, "Already open"); 047 Assert.noNulls(exec); 048 this.inputFeeder = getInputFeeder(context, exec); 049 this.outputPumper = new StreamPumper(exec.getInputStream(), encoding, context.getStdout()); 050 this.errorPumper = new StreamPumper(exec.getErrStream(), encoding, context.getStderr()); 051 this.open = true; 052 } 053 054 public void startPumping() { 055 Assert.isTrue(open, "Not open"); 056 Assert.isFalse(pumping, "Already pumping"); 057 Assert.noNulls(inputFeeder, outputPumper, errorPumper); 058 if (inputFeeder.isPresent()) { 059 inputFeeder.get().start(); 060 } 061 errorPumper.start(); 062 outputPumper.start(); 063 this.pumping = true; 064 } 065 066 public void waitUntilDone() throws InterruptedException { 067 Assert.isTrue(open, "Not open"); 068 Assert.isTrue(pumping, "Not pumping"); 069 Assert.isFalse(done, "Already done"); 070 if (inputFeeder.isPresent()) { 071 inputFeeder.get().waitUntilDone(); 072 } 073 outputPumper.waitUntilDone(); 074 errorPumper.waitUntilDone(); 075 this.done = true; 076 } 077 078 public void validate() throws InterruptedException { 079 Assert.isTrue(done, "Not done"); 080 if (outputPumper.getException() != null) { 081 throw new IllegalStateException("Error inside systemOut parser", outputPumper.getException()); 082 } 083 if (errorPumper.getException() != null) { 084 throw new IllegalStateException("Error inside systemErr parser", errorPumper.getException()); 085 } 086 } 087 088 public void disableQuietly() { 089 if (inputFeeder != null && inputFeeder.isPresent()) { 090 inputFeeder.get().disable(); 091 } 092 if (errorPumper != null) { 093 errorPumper.disable(); 094 } 095 if (outputPumper != null) { 096 outputPumper.disable(); 097 } 098 } 099 100 public void closeQuietly() { 101 if (inputFeeder != null && inputFeeder.isPresent()) { 102 inputFeeder.get().close(); 103 } 104 if (errorPumper != null) { 105 errorPumper.close(); 106 } 107 if (outputPumper != null) { 108 outputPumper.close(); 109 } 110 } 111 112 public CommandContext getContext() { 113 return context; 114 } 115 116 protected Optional<StreamFeeder> getInputFeeder(CommandContext context, ChannelExec exec) throws IOException { 117 if (context.getStdin().isPresent()) { 118 StreamFeeder feeder = new StreamFeeder(context.getStdin().get(), exec.getOutputStream()); 119 return Optional.of(feeder); 120 } else { 121 return Optional.absent(); 122 } 123 } 124 125}