Download | Plain Text | No Line Numbers


  1. /*
  2.  * Copyright (c) 2010, Manuel Mausz. All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * - Redistributions of source code must retain the above copyright
  9.  * notice, this list of conditions and the following disclaimer.
  10.  *
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  * notice, this list of conditions and the following disclaimer in the
  13.  * documentation and/or other materials provided with the distribution.
  14.  *
  15.  * - The names of the authors may not be used to endorse or promote products
  16.  * derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  19.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  20.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  21.  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  22.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  23.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  25.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  26.  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  27.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  */
  30.  
  31. import static java.lang.System.err;
  32. import static java.lang.System.out;
  33.  
  34. import java.util.Arrays;
  35.  
  36. import java.net.*;
  37. import java.io.*;
  38.  
  39. /*
  40.  * Client implementation for Lab#1 of DSLab WS10
  41.  * See angabe.pdf for details
  42.  *
  43.  * This code is not documented at all. This is volitional
  44.  *
  45.  * @author Manuel Mausz (0728348)
  46.  */
  47. public class Client
  48. {
  49. public class ProxyConnection
  50. extends CommandNetwork
  51. implements Runnable
  52. {
  53. private final ObjectInputStream oin;
  54. private final String downloadDir;
  55. private final Object mainLock;
  56. private final Object intLock;
  57.  
  58. ProxyConnection(InputStream in, String downloadDir,
  59. Object mainLock, Object intLock)
  60. throws NoSuchMethodException, IOException
  61. {
  62. this.oin = new ObjectInputStream(new BufferedInputStream(in));
  63. this.downloadDir = downloadDir;
  64. this.mainLock = mainLock;
  65. this.intLock = intLock;
  66.  
  67. cmdHandler.register("!error", this, "cmdError");
  68. cmdHandler.register("!output", this, "cmdOutput");
  69. cmdHandler.register("!download", this, "cmdDownload");
  70. cmdHandler.register("unknown", this, "cmdUnknown");
  71. }
  72.  
  73. /*------------------------------------------------------------------------*/
  74.  
  75. public void notifyInteractive()
  76. {
  77. synchronized(intLock)
  78. {
  79. intLock.notify();
  80. }
  81. }
  82.  
  83. /*------------------------------------------------------------------------*/
  84.  
  85. public void cmdPrintOutput(PrintStream out, String cmd, String[] args)
  86. throws IOException
  87. {
  88. long num;
  89. if ((num = Utils.parseHeaderNum(args, 0)) < 0)
  90. return;
  91.  
  92. String msg;
  93. for (; num > 0 && (msg = oin.readUTF()) != null; --num)
  94. out.println(msg);
  95. notifyInteractive();
  96. }
  97.  
  98. /*------------------------------------------------------------------------*/
  99.  
  100. public void cmdError(String cmd, String[] args)
  101. throws IOException
  102. {
  103. cmdPrintOutput(err, cmd, args);
  104. }
  105.  
  106. /*------------------------------------------------------------------------*/
  107.  
  108. public void cmdOutput(String cmd, String[] args)
  109. throws IOException
  110. {
  111. cmdPrintOutput(out, cmd, args);
  112. }
  113.  
  114. /*------------------------------------------------------------------------*/
  115.  
  116. public void cmdDownload(String cmd, String[] args)
  117. {
  118. if (args.length < 2 || args[1].length() <= 0)
  119. {
  120. err.println("Error: Invalid " + cmd + "-command paket from proxy. Ignoring...");
  121. notifyInteractive();
  122. return;
  123. }
  124.  
  125. long filesize;
  126. if ((filesize = Utils.parseHeaderNum(args, 1)) < 0)
  127. return;
  128.  
  129. File file = new File(downloadDir, args[0]);
  130. file.delete();
  131. try
  132. {
  133. FileOutputStream fout = null;
  134. try
  135. {
  136. fout = new FileOutputStream(file);
  137. }
  138. catch(FileNotFoundException e)
  139. {
  140. err.println("Error: Unable to write to file '" + file + "': " + e.getMessage());
  141. }
  142.  
  143. byte[] buffer = new byte[8 * 1024];
  144. int toread = buffer.length;
  145. while(filesize > 0)
  146. {
  147. if (filesize < toread)
  148. toread = (int) filesize;
  149. int count = oin.read(buffer, 0, toread);
  150. if (count == -1)
  151. throw new IOException("Connection reset by peer");
  152. if (fout != null)
  153. fout.write(buffer, 0, count);
  154. filesize -= count;
  155. }
  156.  
  157. if (fout != null)
  158. out.println("File '" + file + "' successfully downloaded.");
  159. }
  160. catch(IOException e)
  161. {
  162. err.println("Error: Error during file transfer: " + e.getMessage());
  163. }
  164. notifyInteractive();
  165. }
  166.  
  167. /*------------------------------------------------------------------------*/
  168.  
  169. public void cmdUnknown(String cmd, String[] args)
  170. {
  171. err.println("Error: Unknown data from proxy: " + cmd + " "
  172. + Utils.join(Arrays.asList(args), " "));
  173. notifyInteractive();
  174. }
  175.  
  176. /*------------------------------------------------------------------------*/
  177.  
  178. public void run()
  179. {
  180. try
  181. {
  182. run(oin);
  183. }
  184. catch(CommandHandler.Exception e)
  185. {
  186. err.println("Internal Error: " + e.getMessage());
  187. }
  188. catch(IOException e)
  189. {
  190. /* ignore that exception
  191.   * thread will shutdown and unlock the main thread
  192.   * which will shutdown the application
  193.   */
  194. }
  195.  
  196. notifyInteractive();
  197. synchronized(mainLock)
  198. {
  199. mainLock.notify();
  200. }
  201. }
  202. }
  203.  
  204. /*==========================================================================*/
  205.  
  206. public class Interactive
  207. extends CommandInteractive
  208. implements Runnable
  209. {
  210. private final InputStream sin;
  211. private final OutputStream sout;
  212. private final ObjectOutputStream oout;
  213. private final Object mainLock;
  214. private final Object intLock;
  215.  
  216. Interactive(InputStream sin, OutputStream sout,
  217. Object mainLock, Object intLock)
  218. throws NoSuchMethodException, IOException
  219. {
  220. this.sin = sin;
  221. this.sout = sout;
  222. this.mainLock = mainLock;
  223. this.intLock = intLock;
  224. this.oout = new ObjectOutputStream(sout);
  225.  
  226. cmdHandler.register("unknown", this, "cmdUnknown");
  227. cmdHandler.register("!exit", this, "cmdExit");
  228. }
  229.  
  230. /*------------------------------------------------------------------------*/
  231.  
  232. public void waitForSocket()
  233. {
  234. synchronized(intLock)
  235. {
  236. try
  237. {
  238. intLock.wait(1000);
  239. }
  240. catch(InterruptedException e)
  241. {
  242. /* if we get interrupted -> ignore */
  243. }
  244. }
  245. }
  246.  
  247. /*------------------------------------------------------------------------*/
  248.  
  249. public void cmdUnknown(String cmd, String[] args)
  250. throws IOException
  251. {
  252. oout.writeUTF(cmd + " " + Utils.join(Arrays.asList(args), " "));
  253. oout.flush();
  254. waitForSocket();
  255. }
  256.  
  257. /*------------------------------------------------------------------------*/
  258.  
  259. public void cmdExit(String cmd, String[] args)
  260. {
  261. stop();
  262. }
  263.  
  264. /*------------------------------------------------------------------------*/
  265.  
  266. public void printPrompt()
  267. {
  268. out.print(">: ");
  269. out.flush();
  270. }
  271.  
  272. /*------------------------------------------------------------------------*/
  273.  
  274. public void shutdown()
  275. {
  276. try
  277. {
  278. oout.flush();
  279. }
  280. catch(IOException e)
  281. {}
  282. }
  283.  
  284. /*------------------------------------------------------------------------*/
  285.  
  286. public void run()
  287. {
  288. try
  289. {
  290. run(sin);
  291. }
  292. catch(CommandHandler.Exception e)
  293. {
  294. err.println("Internal Error: " + e.getMessage());
  295. }
  296. catch(IOException e)
  297. {
  298. /* ignore that exception
  299.   * thread will shutdown and unlock the main thread
  300.   * which will shutdown the application
  301.   */
  302. }
  303.  
  304. shutdown();
  305. synchronized(mainLock)
  306. {
  307. mainLock.notify();
  308. }
  309. }
  310. }
  311.  
  312. /*==========================================================================*/
  313.  
  314. private static String downloadDir;
  315. private static String proxyHost;
  316. private static int proxyTCPPort;
  317. private Socket sock = null;
  318. private InputStream sockin = null;
  319. private OutputStream sockout = null;
  320. private Thread tPConnection = null;
  321. private Thread tInteractive = null;
  322. private InputStream stdin = null;
  323. private final Object interactiveLock = new Object();
  324. private final Object mainLock = new Object();
  325.  
  326. /*--------------------------------------------------------------------------*/
  327.  
  328. public static void usage()
  329. throws Utils.Shutdown
  330. {
  331. out.println("Usage: Client downloadDir proxyHost proxyTCPPort\n");
  332. out.println("downloadDir\t...the directory to put downloaded files");
  333. out.println("proxyHost\t...the host name or an IP address where the Proxy is running");
  334. out.println("proxyTCPPort\t...the TCP port where the server is listening for client connections");
  335.  
  336. // Java is some piece of crap which doesn't allow me to set exitcode w/o
  337. // using System.exit. Maybe someday Java will be a fully functional
  338. // programming language, but I wouldn't bet my money
  339. //System.exit(1);
  340. throw new Utils.Shutdown("FUCK YOU JAVA");
  341. }
  342.  
  343. /*--------------------------------------------------------------------------*/
  344.  
  345. public void bailout(String error)
  346. throws Utils.Shutdown
  347. {
  348. err.println("Error: " + error);
  349. shutdown();
  350.  
  351. // Java is some piece of crap which doesn't allow me to set exitcode w/o
  352. // using System.exit. Maybe someday Java will be a fully functional
  353. // programming language, but I wouldn't bet my money
  354. //System.exit(2);
  355. throw new Utils.Shutdown("FUCK YOU JAVA");
  356. }
  357.  
  358. /*--------------------------------------------------------------------------*/
  359.  
  360. public void parseArgs(String[] args)
  361. {
  362. if (args.length != 3)
  363. usage();
  364.  
  365. downloadDir = args[0];
  366. File dldir = new File(downloadDir);
  367. if (!dldir.isDirectory())
  368. bailout("downloadDir '" + downloadDir + "' is not a directory");
  369. if (!dldir.canWrite())
  370. bailout("downloadDir '" + downloadDir + "' is not writeable");
  371.  
  372. proxyHost = args[1];
  373. if (proxyHost.length() == 0)
  374. bailout("proxyHost is empty");
  375.  
  376. try
  377. {
  378. proxyTCPPort = Integer.parseInt(args[2]);
  379. if (proxyTCPPort <= 0 || proxyTCPPort > 65536)
  380. bailout("proxyTCPPort must be a valid port number (1 - 65535)");
  381. }
  382. catch(NumberFormatException e)
  383. {
  384. bailout("proxyTCPPort must be numeric");
  385. }
  386. }
  387.  
  388. /*--------------------------------------------------------------------------*/
  389.  
  390. public void shutdown()
  391. {
  392. try
  393. {
  394. if (sockin != null)
  395. sockin.close();
  396. }
  397. catch(IOException e)
  398. {}
  399.  
  400. try
  401. {
  402. if (sockout != null)
  403. sockout.close();
  404. }
  405. catch(IOException e)
  406. {}
  407.  
  408. try
  409. {
  410. if (sock != null && !sock.isClosed())
  411. sock.close();
  412. }
  413. catch(IOException e)
  414. {}
  415.  
  416. try
  417. {
  418. if (tPConnection != null)
  419. tPConnection.join();
  420. }
  421. catch(InterruptedException e)
  422. {}
  423.  
  424. try
  425. {
  426. if (tInteractive != null)
  427. {
  428. tInteractive.interrupt();
  429. tInteractive.join();
  430. }
  431. }
  432. catch(InterruptedException e)
  433. {}
  434.  
  435. try
  436. {
  437. if (stdin != null)
  438. stdin.close();
  439. }
  440. catch(IOException e)
  441. {}
  442. }
  443.  
  444. /*--------------------------------------------------------------------------*/
  445.  
  446. public void run(String[] args)
  447. {
  448. parseArgs(args);
  449.  
  450. try
  451. {
  452. out.println("Connecting to " + proxyHost + ":" + proxyTCPPort + "...");
  453. sock = new Socket(proxyHost, proxyTCPPort);
  454. sockin = sock.getInputStream();
  455. sockout = sock.getOutputStream();
  456. out.println("Connected...");
  457. }
  458. catch(UnknownHostException e)
  459. {
  460. bailout("Unable to resolve hostname: " + e.getMessage());
  461. }
  462. catch(IOException e)
  463. {
  464. bailout("Unable to connect to proxy: " + e.getMessage());
  465. }
  466.  
  467. synchronized(mainLock)
  468. {
  469. try
  470. {
  471. tPConnection = new Thread(new ProxyConnection(sockin, downloadDir,
  472. mainLock, interactiveLock));
  473. tPConnection.start();
  474. }
  475. catch(NoSuchMethodException e)
  476. {
  477. bailout("Unable to setup remote command handler");
  478. }
  479. catch(IOException e)
  480. {
  481. bailout("Unable to create object input stream: " + e.getMessage());
  482. }
  483.  
  484. try
  485. {
  486. InputStream stdin = java.nio.channels.Channels.newInputStream(
  487. new FileInputStream(FileDescriptor.in).getChannel());
  488. tInteractive = new Thread(new Interactive(stdin, sockout,
  489. mainLock, interactiveLock));
  490. tInteractive.start();
  491. }
  492. catch(NoSuchMethodException e)
  493. {
  494. bailout("Unable to setup interactive command handler");
  495. }
  496. catch(IOException e)
  497. {
  498. bailout("Unable to create object output stream: " + e.getMessage());
  499. }
  500.  
  501. out.println("Client startup successful!");
  502. try
  503. {
  504. mainLock.wait();
  505. }
  506. catch(InterruptedException e)
  507. {
  508. /* if we get interrupted -> ignore */
  509. }
  510.  
  511. try
  512. {
  513. /* let the threads shutdown */
  514. Thread.sleep(100);
  515. }
  516. catch(InterruptedException e)
  517. {}
  518. }
  519.  
  520. if (tPConnection != null && !tPConnection.isAlive())
  521. bailout("Connection to proxy closed unexpected. Terminating...");
  522.  
  523. shutdown();
  524. }
  525.  
  526. /*--------------------------------------------------------------------------*/
  527.  
  528. public static void main(String[] args)
  529. {
  530. try
  531. {
  532. Client cli = new Client();
  533. cli.run(args);
  534. }
  535. catch(Utils.Shutdown e)
  536. {}
  537. }
  538. }
  539.