Use log4j for logging.
[xudocci.git] / src / main / java / net / pterodactylus / irc / DccReceiver.java
1 /*
2  * XdccDownloader - DccReceiver.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.irc;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.net.InetAddress;
24 import java.net.Socket;
25 import java.util.concurrent.TimeUnit;
26
27 import net.pterodactylus.irc.event.DccDownloadFailed;
28 import net.pterodactylus.irc.event.DccDownloadFinished;
29 import net.pterodactylus.irc.event.DccSendReceived;
30 import net.pterodactylus.xdcc.util.io.BandwidthCountingInputStream;
31
32 import com.google.common.eventbus.EventBus;
33 import com.google.common.io.Closeables;
34 import com.google.common.util.concurrent.AbstractExecutionThreadService;
35 import org.apache.log4j.Logger;
36
37 /**
38  * Service that receives a file offered by a {@link DccSendReceived}.
39  *
40  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
41  */
42 public class DccReceiver extends AbstractExecutionThreadService {
43
44         /** The logger. */
45         private static final Logger logger = Logger.getLogger(DccReceiver.class.getName());
46
47         /** The event bus. */
48         private final EventBus eventBus;
49
50         /** The address to connect to. */
51         private final InetAddress inetAddress;
52
53         /** The port number to connect to. */
54         private final int port;
55
56         /** The name of the file being offered. */
57         private final String filename;
58
59         /** The size of the file being offered. */
60         private final long size;
61
62         /** The output stream to write the file to. */
63         private final OutputStream outputStream;
64
65         /** The number of bytes already written. */
66         private long progress;
67
68         /** The bandwidth-measuring input stream. */
69         private BandwidthCountingInputStream inputStream;
70
71         /**
72          * Creates a new DCC receiver.
73          *
74          * @param inetAddress
75          *              The address to connect to
76          * @param port
77          *              The port number to connect to
78          * @param filename
79          *              The name of the file being downloaded
80          * @param size
81          *              The size of the file being downloaded, or {@code -1} if the size is not
82          *              known
83          * @param outputStream
84          *              The output stream to write the file to
85          */
86         public DccReceiver(EventBus eventBus, InetAddress inetAddress, int port, String filename, long size, OutputStream outputStream) {
87                 this(eventBus, inetAddress, port, filename, 0, size, outputStream);
88         }
89
90         /**
91          * Creates a new DCC receiver.
92          *
93          * @param inetAddress
94          *              The address to connect to
95          * @param port
96          *              The port number to connect to
97          * @param filename
98          *              The name of the file being downloaded
99          * @param startOffset
100          *              The offset at which the download starts in case of a resume
101          * @param size
102          *              The size of the file being downloaded, or {@code -1} if the size is not
103          *              known
104          * @param outputStream
105          *              The output stream to write the file to
106          */
107         public DccReceiver(EventBus eventBus, InetAddress inetAddress, int port, String filename, long startOffset, long size, OutputStream outputStream) {
108                 this.eventBus = eventBus;
109                 this.inetAddress = inetAddress;
110                 this.port = port;
111                 this.filename = filename;
112                 this.progress = startOffset;
113                 this.size = size;
114                 this.outputStream = outputStream;
115         }
116
117         //
118         // ACCESSORS
119         //
120
121         /**
122          * Returns the name of the file being downloaded. The name is not used by the
123          * DCC receiver, it only serves as a kind of identifier.
124          *
125          * @return The name of the file being downloaded
126          */
127         public String filename() {
128                 return filename;
129         }
130
131         /**
132          * Returns the size of the file being downloaded. If the size of the file is
133          * not known, {@code -1} is returned.
134          *
135          * @return The size of the file being downloaded, or {@code -1} if the size is
136          *         not known
137          */
138         public long size() {
139                 return size;
140         }
141
142         /**
143          * Returns the number of bytes that have already been downloaded.
144          *
145          * @return The number of bytes that have already been downloaded
146          */
147         public long progress() {
148                 return progress;
149         }
150
151         /**
152          * Returns the current rate of the download.
153          *
154          * @return The current rate of the download, in bytes/second
155          */
156         public long currentRate() {
157                 return (inputStream != null) ? inputStream.getCurrentRate() : 0;
158         }
159
160         /**
161          * Returns the overall rate of the download.
162          *
163          * @return The overall rate of the download, in bytes/second
164          */
165         public long overallRate() {
166                 return (inputStream != null) ? inputStream.getOverallRate() : 0;
167         }
168
169         //
170         // ABSTRACTEXECUTIONTHREADSERVICE METHODS
171         //
172
173         @Override
174         protected void run() throws IOException {
175                 Socket socket = null;
176                 try {
177                         socket = new Socket(inetAddress, port);
178                         socket.setSoTimeout((int) TimeUnit.MINUTES.toMillis(3));
179                         InputStream socketInputStream = socket.getInputStream();
180                         inputStream = new BandwidthCountingInputStream(socketInputStream, 5, TimeUnit.SECONDS);
181                         byte[] buffer = new byte[65536];
182                         while (isRunning() && ((size == -1) || (progress < size))) {
183                                 int r = inputStream.read(buffer);
184                                 if (r == -1) {
185                                         /* yay, eof! */
186                                         break;
187                                 }
188                                 outputStream.write(buffer, 0, r);
189                                 progress += r;
190                         }
191                         outputStream.flush();
192                         if ((size == -1) || (progress == size)) {
193                                 eventBus.post(new DccDownloadFinished(this));
194                         } else {
195                                 eventBus.post(new DccDownloadFailed(this, new IOException("Download aborted.")));
196                         }
197                 } catch (IOException ioe1) {
198                         logger.warn("I/O error while receiving DCC!", ioe1);
199                         eventBus.post(new DccDownloadFailed(this, ioe1));
200                 } finally {
201                         Closeables.close(inputStream, true);
202                         if (socket != null) {
203                                 socket.close();
204                         }
205                 }
206         }
207
208 }