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.model;
017
018import java.io.ByteArrayInputStream;
019import java.io.InputStream;
020import java.util.List;
021
022import org.codehaus.plexus.util.cli.StreamConsumer;
023import org.kuali.common.util.Assert;
024import org.kuali.common.util.Encodings;
025import org.kuali.common.util.Str;
026import org.kuali.common.util.stream.NoOpStreamConsumer;
027
028import com.google.common.base.Optional;
029import com.google.common.collect.ImmutableList;
030
031public final class CommandContext {
032
033        private final byte[] command;
034        private final Optional<InputStream> stdin;
035        private final Optional<Integer> timeout;
036        private final StreamConsumer stdout;
037        private final StreamConsumer stderr;
038        private final List<Integer> successCodes;
039        private final boolean ignoreExitValue;
040
041        public static Builder builder(String command) {
042                return new Builder(command);
043        }
044
045        public static class Builder {
046
047                private static final int SUCCESS = 0;
048                private static final String UTF8 = Encodings.UTF8;
049
050                // Required
051                private final byte[] command;
052
053                // Optional
054                private Optional<InputStream> stdin = Optional.absent(); // Don't supply anything to stdin by default
055                private Optional<Integer> timeout = Optional.absent(); // Default is to wait forever
056                private StreamConsumer stdout = NoOpStreamConsumer.INSTANCE; // Ignore anything produced on stdout, by default
057                private StreamConsumer stderr = NoOpStreamConsumer.INSTANCE; // Ignore anything produced on stderr, by default
058                private List<Integer> successCodes = ImmutableList.of(SUCCESS); // Expect zero as an exit value
059                private boolean ignoreExitValue = false; // Set this to true if you want to ignore the exit value of the process
060
061                /**
062                 * Pass this command to a remote server as a sequence of bytes encoded using UTF-8
063                 */
064                public Builder(String command) {
065                        this(command, UTF8);
066                }
067
068                /**
069                 * Pass this command to a remote server as a sequence of bytes encoded using the indicated encoding
070                 */
071                public Builder(String command, String encoding) {
072                        this(Str.getBytes(command, encoding));
073                }
074
075                public Builder(byte[] command) {
076                        this.command = command;
077                }
078
079                /**
080                 * Convert the string into a <code>byte[]</code> using UTF-8 encoding
081                 */
082                public Builder stdin(String stdin) {
083                        return stdin(stdin, UTF8);
084                }
085
086                /**
087                 * Convert the string into a <code>byte[]</code> using the indicated encoding
088                 */
089                public Builder stdin(String stdin, String encoding) {
090                        Assert.noBlanks(stdin, encoding);
091                        byte[] bytes = Str.getBytes(stdin, encoding);
092                        return stdin(new ByteArrayInputStream(bytes));
093                }
094
095                public Builder stdin(InputStream stdin) {
096                        this.stdin = Optional.of(stdin);
097                        return this;
098                }
099
100                public Builder timeout(int timeout) {
101                        this.timeout = Optional.of(timeout);
102                        return this;
103                }
104
105                public Builder stdout(StreamConsumer stdout) {
106                        this.stdout = stdout;
107                        return this;
108                }
109
110                public Builder stderr(StreamConsumer stderr) {
111                        this.stderr = stderr;
112                        return this;
113                }
114
115                public Builder ignoreExitValue(boolean ignoreExitValue) {
116                        this.ignoreExitValue = ignoreExitValue;
117                        return this;
118                }
119
120                public Builder successCodes(List<Integer> successCodes) {
121                        this.successCodes = successCodes;
122                        return this;
123                }
124
125                public CommandContext build() {
126                        Assert.noNulls(command, stdin, timeout, stdout, stderr, successCodes);
127                        if (timeout.isPresent()) {
128                                Assert.notNegative(timeout.get());
129                        }
130                        this.successCodes = ImmutableList.copyOf(successCodes);
131                        return new CommandContext(this);
132                }
133
134        }
135
136        private CommandContext(Builder builder) {
137                this.command = builder.command;
138                this.stdin = builder.stdin;
139                this.timeout = builder.timeout;
140                this.stderr = builder.stderr;
141                this.stdout = builder.stdout;
142                this.successCodes = builder.successCodes;
143                this.ignoreExitValue = builder.ignoreExitValue;
144
145        }
146
147        public byte[] getCommand() {
148                return command;
149        }
150
151        public Optional<InputStream> getStdin() {
152                return stdin;
153        }
154
155        public Optional<Integer> getTimeout() {
156                return timeout;
157        }
158
159        public StreamConsumer getStdout() {
160                return stdout;
161        }
162
163        public StreamConsumer getStderr() {
164                return stderr;
165        }
166
167        public List<Integer> getSuccessCodes() {
168                return successCodes;
169        }
170
171        public boolean isIgnoreExitValue() {
172                return ignoreExitValue;
173        }
174
175}