Initialize logger with real name of implementing class.
[sonitus.git] / src / main / java / net / pterodactylus / sonitus / data / filter / ExternalFilter.java
1 /*
2  * Sonitus - ExternalFilter.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sonitus.data.filter;
19
20 import java.io.EOFException;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.io.PipedInputStream;
25 import java.io.PipedOutputStream;
26 import java.util.Arrays;
27 import java.util.logging.Logger;
28
29 import net.pterodactylus.sonitus.data.ConnectException;
30 import net.pterodactylus.sonitus.data.Connection;
31 import net.pterodactylus.sonitus.data.Filter;
32 import net.pterodactylus.sonitus.data.Format;
33 import net.pterodactylus.sonitus.data.Source;
34
35 import com.google.common.base.Preconditions;
36 import com.google.common.collect.ImmutableList;
37 import com.google.common.collect.Iterables;
38 import com.google.common.io.ByteStreams;
39
40 /**
41  * {@link Filter} implementation that runs its {@link Source} through an
42  * external program.
43  *
44  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
45  */
46 public abstract class ExternalFilter implements Filter {
47
48         /** The logger. */
49         private final Logger logger = Logger.getLogger(getClass().getName());
50
51         /** The format of the source. */
52         private Format format;
53
54         /** The input stream that will hold the converted source. */
55         private PipedInputStream pipedInputStream;
56
57         //
58         // FILTER METHODS
59
60         @Override
61         public Format format() {
62                 return format;
63         }
64
65         @Override
66         public byte[] get(int bufferSize) throws EOFException, IOException {
67                 byte[] buffer = new byte[bufferSize];
68                 int read = pipedInputStream.read(buffer);
69                 return Arrays.copyOf(buffer, read);
70         }
71
72         @Override
73         public void connect(Source source) throws ConnectException {
74                 Preconditions.checkNotNull(source, "source must not be null");
75
76                 format = source.format();
77                 try {
78                         final Process process = Runtime.getRuntime().exec(Iterables.toArray(ImmutableList.<String>builder().add(binary(format)).addAll(parameters(format)).build(), String.class));
79                         final InputStream processOutput = process.getInputStream();
80                         final OutputStream processInput = process.getOutputStream();
81                         final InputStream processError = process.getErrorStream();
82                         final PipedOutputStream pipedOutputStream = new PipedOutputStream();
83                         pipedInputStream = new PipedInputStream(pipedOutputStream);
84                         new Thread(new Runnable() {
85
86                                 @Override
87                                 public void run() {
88                                         try {
89                                                 drainInputStream(processError);
90                                         } catch (IOException ioe1) {
91                                                 /* ignore, just let the thread exit. */
92                                         }
93                                         logger.finest("ExternalFilter: Reading stderr finished.");
94                                 }
95                         }).start();
96                         new Thread(new Runnable() {
97
98                                 @Override
99                                 public void run() {
100                                         try {
101                                                 ByteStreams.copy(processOutput, pipedOutputStream);
102                                         } catch (IOException ioe1) {
103                                                 /* okay, just exit. */
104                                         }
105                                         logger.finest("ExternalFilter: Reading stdout finished.");
106                                 }
107                         }).start();
108                         new Thread(new Connection(source) {
109
110                                 @Override
111                                 protected int bufferSize() {
112                                         return 4096;
113                                 }
114
115                                 @Override
116                                 protected void feed(byte[] buffer) throws IOException {
117                                         processInput.write(buffer);
118                                         processInput.flush();
119                                 }
120
121                                 @Override
122                                 protected void finish() throws IOException {
123                                         processInput.close();
124                                         processOutput.close();
125                                         processError.close();
126                                 }
127                         }).start();
128                 } catch (IOException ioe1) {
129
130                 }
131         }
132
133         //
134         // SUBCLASS METHODS
135         //
136
137         /**
138          * Returns the location of the binary to execute.
139          *
140          * @param format
141          *              The format being processed
142          * @return The location of the binary to execute
143          */
144         protected abstract String binary(Format format);
145
146         /**
147          * Returns the parameters for the binary.
148          *
149          * @param format
150          *              The format being processed
151          * @return The parameters for the binary
152          */
153         protected abstract Iterable<String> parameters(Format format);
154
155         //
156         // STATIC METHODS
157         //
158
159         private static void drainInputStream(InputStream inputStream) throws IOException {
160                 byte[] buffer = new byte[4096];
161                 int read;
162                 while ((read = inputStream.read(buffer)) != -1) {
163                         logger.finest(String.format("ExternalFilter: Drained %d Bytes.", read));
164                         /* do nothing, just read the damn thing. */
165                 }
166         }
167
168 }