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.text.SimpleDateFormat;
  35. import java.text.ParseException;
  36.  
  37. import java.rmi.registry.*;
  38. import java.rmi.server.*;
  39. import java.rmi.*;
  40.  
  41. import java.util.concurrent.ScheduledExecutorService;
  42. import java.util.concurrent.ScheduledFuture;
  43. import java.util.concurrent.Executors;
  44. import java.util.concurrent.TimeUnit;
  45. import java.util.Collections;
  46. import java.util.Properties;
  47. import java.util.HashSet;
  48. import java.util.Arrays;
  49. import java.util.Date;
  50. import java.util.Map;
  51.  
  52. import java.io.*;
  53.  
  54. /*
  55.  * Client implementation for Lab#2 of DSLab WS10
  56.  * See angabe.pdf for details
  57.  *
  58.  * This code is not documented at all. This is volitional
  59.  *
  60.  * @author Manuel Mausz (0728348)
  61.  */
  62. public class Client
  63. {
  64. public class WaitForServer
  65. implements Runnable
  66. {
  67. public void shutdown()
  68. {
  69. scheduler.shutdown();
  70. synchronized(mainLock)
  71. {
  72. mainLock.notify();
  73. }
  74. }
  75.  
  76. public void run()
  77. {
  78. synchronized(registry)
  79. {
  80. try
  81. {
  82. int i;
  83. String[] tmp = registry.list();
  84. if ((i = Arrays.asList(tmp).indexOf(serverName)) != -1)
  85. {
  86. c2siface = ((RemoteInterface)registry.lookup(Arrays.asList(tmp).get(i))).c2sInterfaceFactory();
  87. if (c2siface != null)
  88. shutdown();
  89. }
  90. }
  91. catch(NotBoundException e)
  92. {
  93. err.println("Error: " + e.getMessage()); /* this shouldn't happen */
  94. shutdown();
  95. }
  96. catch(RemoteException e)
  97. {
  98. err.println("Unable to lookup servers: " + e.getMessage());
  99. shutdown();
  100. }
  101. }
  102. }
  103. }
  104.  
  105. /*==========================================================================*/
  106.  
  107. public class Interactive
  108. extends CommandInteractive
  109. implements Runnable
  110. {
  111. private final InputStream sin;
  112. private final Object mainLock;
  113. private boolean loggedin = false;
  114.  
  115. Interactive(InputStream sin, Object mainLock)
  116. throws NoSuchMethodException, IOException
  117. {
  118. this.sin = sin;
  119. this.mainLock = mainLock;
  120.  
  121. cmdHandler.register("!register", this, "cmdRegister");
  122. cmdHandler.register("!login", this, "cmdLogin");
  123. cmdHandler.register("!create", this, "cmdCreate");
  124. cmdHandler.register("!addDate", this, "cmdAddDate");
  125. cmdHandler.register("!invite", this, "cmdInvite");
  126. cmdHandler.register("!get", this, "cmdGet");
  127. cmdHandler.register("!vote", this, "cmdVote");
  128. cmdHandler.register("!finalize", this, "cmdFinalize");
  129. cmdHandler.register("!logout", this, "cmdLogout");
  130. cmdHandler.register("!exit", this, "cmdExit");
  131. cmdHandler.register("!help", this, "cmdHelp");
  132. cmdHandler.register("unknown", this, "cmdUnknown");
  133. }
  134.  
  135. /*------------------------------------------------------------------------*/
  136.  
  137. public void cmdRegister(String cmd, String[] args)
  138. {
  139. if (args.length != 2)
  140. {
  141. err.println("Invalid Syntax: " + cmd + " <username> <password>");
  142. return;
  143. }
  144.  
  145. try
  146. {
  147. if (c2siface.register(args[0], args[1]))
  148. out.println("Successfully registered.");
  149. else
  150. err.println("Username already registered.");
  151. }
  152. catch(RemoteException e)
  153. {
  154. err.println("Error: " + e.getCause().getMessage());
  155. }
  156. }
  157.  
  158. /*------------------------------------------------------------------------*/
  159.  
  160. public void cmdLogin(String cmd, String[] args)
  161. {
  162. if (args.length != 2)
  163. {
  164. err.println("Invalid Syntax: " + cmd + " <username> <password>");
  165. return;
  166. }
  167.  
  168. try
  169. {
  170. if (c2siface.login(args[0], args[1], s2ciface))
  171. {
  172. out.println("Successfully logged in.");
  173. loggedin = true;
  174. }
  175. else
  176. err.println("Wrong username or password.");
  177. }
  178. catch(RemoteException e)
  179. {
  180. err.println("Error: " + e.getCause().getMessage());
  181. }
  182. }
  183.  
  184. /*------------------------------------------------------------------------*/
  185.  
  186. public void cmdCreate(String cmd, String[] args)
  187. {
  188. if (args.length != 3)
  189. {
  190. err.println("Invalid Syntax: " + cmd + " <name> <location> <duration in minutes>");
  191. return;
  192. }
  193.  
  194. int duration = 0;
  195. try
  196. {
  197. duration = Integer.parseInt(args[2]);
  198. if (duration <= 0)
  199. {
  200. err.println("Error: Invalid argument: Duration must be a positive number.");
  201. return;
  202. }
  203. }
  204. catch(NumberFormatException e)
  205. {
  206. err.println("Error: Invalid argument: Duration must be numeric.");
  207. return;
  208. }
  209.  
  210. try
  211. {
  212. if (c2siface.create(args[0], args[1], duration))
  213. out.println("Event created successfully.");
  214. else
  215. err.println("Unable to create Event. Event already exists.");
  216. }
  217. catch(RemoteException e)
  218. {
  219. err.println("Error: " + e.getCause().getMessage());
  220. }
  221. }
  222.  
  223. /*------------------------------------------------------------------------*/
  224.  
  225. public void cmdAddDate(String cmd, String[] args)
  226. {
  227. SimpleDateFormat datefmt = new SimpleDateFormat("dd.MM.yyyy/HH:mm");
  228. if (args.length != 2)
  229. {
  230. err.println("Invalid Syntax: " + cmd + " <name of event> <date: " + datefmt.toPattern() + ">");
  231. return;
  232. }
  233.  
  234. Date date = null;
  235. try
  236. {
  237. date = datefmt.parse(args[1]);
  238. }
  239. catch(ParseException e)
  240. {}
  241. if (date == null)
  242. {
  243. err.println("Error: Invalid date argument. Syntax: <" + datefmt.toPattern() + ">.");
  244. return;
  245. }
  246.  
  247. try
  248. {
  249. c2siface.addDate(args[0], date);
  250. out.println("Date option added.");
  251. }
  252. catch(RemoteException e)
  253. {
  254. err.println("Error: Unable to add date: " + e.getCause().getMessage());
  255. }
  256. }
  257.  
  258. /*------------------------------------------------------------------------*/
  259.  
  260. public void cmdInvite(String cmd, String[] args)
  261. {
  262. if (args.length != 2)
  263. {
  264. err.println("Invalid Syntax: " + cmd + " <name of event> <username>");
  265. return;
  266. }
  267.  
  268. try
  269. {
  270. c2siface.invite(args[0], args[1]);
  271. out.println("User invited.");
  272. }
  273. catch(RemoteException e)
  274. {
  275. err.println("Error: Unable to invite user: " + e.getCause().getMessage());
  276. }
  277. }
  278.  
  279. /*------------------------------------------------------------------------*/
  280.  
  281. public void cmdGet(String cmd, String[] args)
  282. {
  283. if (args.length != 1)
  284. {
  285. err.println("Invalid Syntax: " + cmd + " <name of event>");
  286. return;
  287. }
  288.  
  289. try
  290. {
  291. out.print(c2siface.event2String(args[0]));
  292. out.flush();
  293. }
  294. catch(RemoteException e)
  295. {
  296. err.println("Error: Unable to fetch event: " + e.getCause().getMessage());
  297. }
  298. }
  299.  
  300. /*------------------------------------------------------------------------*/
  301.  
  302. public void cmdVote(String cmd, String[] args)
  303. {
  304. SimpleDateFormat datefmt = new SimpleDateFormat("dd.MM.yyyy/HH:mm");
  305. if (args.length < 2)
  306. {
  307. err.println("Invalid Syntax: " + cmd + " <name of event> <date: " + datefmt.toPattern() + "> ...");
  308. return;
  309. }
  310.  
  311. int i = 1;
  312. HashSet<Date> dates = new HashSet<Date>();
  313.  
  314. try
  315. {
  316. for(i = 1; i < args.length; ++i)
  317. dates.add(datefmt.parse(args[i]));
  318. }
  319. catch(ParseException e)
  320. {
  321. err.println("Error: Invalid date \"" + args[i] + "\". Syntax: <" + datefmt.toPattern() + ">.");
  322. return;
  323. }
  324.  
  325. try
  326. {
  327. c2siface.vote(args[0], dates.toArray(new Date[dates.size()]));
  328. out.println("Vote registered.");
  329. }
  330. catch(RemoteException e)
  331. {
  332. err.println("Error: Unable to vote: " + e.getCause().getMessage());
  333. }
  334. }
  335.  
  336. /*------------------------------------------------------------------------*/
  337.  
  338. public void cmdFinalize(String cmd, String[] args)
  339. {
  340. if (args.length != 1)
  341. {
  342. err.println("Invalid Syntax: " + cmd + " <name of event>");
  343. return;
  344. }
  345.  
  346. try
  347. {
  348. c2siface.finalize(args[0]);
  349. }
  350. catch(RemoteException e)
  351. {
  352. err.println("Error: Unable to finalize: " + e.getCause().getMessage());
  353. }
  354. }
  355.  
  356. /*------------------------------------------------------------------------*/
  357.  
  358. public void cmdLogout(String cmd, String[] args)
  359. {
  360. if (args.length != 0)
  361. {
  362. err.println("Invalid Syntax: " + cmd);
  363. return;
  364. }
  365.  
  366. try
  367. {
  368. c2siface.logout();
  369. loggedin = false;
  370. out.println("You have been logged out.");
  371. }
  372. catch(RemoteException e)
  373. {
  374. err.println("Error: " + e.getCause().getMessage());
  375. }
  376. }
  377.  
  378. /*------------------------------------------------------------------------*/
  379.  
  380. public void cmdExit(String cmd, String[] args)
  381. {
  382. if (loggedin)
  383. cmdLogout("!logout", new String[0]);
  384. stop();
  385. }
  386.  
  387. /*------------------------------------------------------------------------*/
  388.  
  389. public void cmdHelp(String cmd, String[] args)
  390. throws IOException
  391. {
  392. for(Map.Entry<String, Object[]> entry : cmdHandler.entrySet())
  393. {
  394. if (entry.getKey().equals("unknown") || entry.getKey().equals("!help"))
  395. continue;
  396. out.println(entry.getKey());
  397. }
  398. }
  399.  
  400. /*------------------------------------------------------------------------*/
  401.  
  402. public void cmdUnknown(String cmd, String[] args)
  403. throws IOException
  404. {
  405. out.println("Error: Unknown command: " + cmd + " " + Utils.join(Arrays.asList(args), " "));
  406. out.println("Try !help for a list of commands");
  407. }
  408.  
  409. /*------------------------------------------------------------------------*/
  410.  
  411. public void printPrompt()
  412. {
  413. out.print(">: ");
  414. out.flush();
  415. }
  416.  
  417. /*------------------------------------------------------------------------*/
  418.  
  419. public void shutdown()
  420. {}
  421.  
  422. /*------------------------------------------------------------------------*/
  423.  
  424. public void run()
  425. {
  426. try
  427. {
  428. run(sin);
  429. }
  430. catch(RemoteException e)
  431. {
  432. err.println("Remote Error: " + e.getMessage());
  433. }
  434. catch(CommandHandler.Exception e)
  435. {
  436. err.println("Internal Error: " + e.getMessage());
  437. }
  438. catch(IOException e)
  439. {
  440. /* ignore that exception
  441.   * thread will shutdown and unlock the main thread
  442.   * which will shutdown the application
  443.   */
  444. }
  445.  
  446. shutdown();
  447. synchronized(mainLock)
  448. {
  449. mainLock.notify();
  450. }
  451. }
  452. }
  453.  
  454. /*==========================================================================*/
  455.  
  456. private static String serverName;
  457. private InputStream stdin = null;
  458. private Thread tInteractive = null;
  459. private ScheduledExecutorService scheduler = null;
  460. private Registry registry = null;
  461. private C2SInterface c2siface = null;
  462. private S2CInterfaceImpl s2ciface = null;
  463. private final Object mainLock = new Object();
  464.  
  465. /*--------------------------------------------------------------------------*/
  466.  
  467. public static void usage()
  468. throws Utils.Shutdown
  469. {
  470. out.println("Usage: Server serverName\n");
  471. out.println("serverName\t...the name of the remote reference in the RMI registry");
  472. out.println("\t\t of the server that shall be responsible for this client.");
  473.  
  474. // Java is some piece of crap which doesn't allow me to set exitcode w/o
  475. // using System.exit. Maybe someday Java will be a fully functional
  476. // programming language, but I wouldn't bet my money
  477. //System.exit(1);
  478. throw new Utils.Shutdown("FUCK YOU JAVA");
  479. }
  480.  
  481. /*--------------------------------------------------------------------------*/
  482.  
  483. public void bailout(String error)
  484. throws Utils.Shutdown
  485. {
  486. if (error != null)
  487. err.println("Error: " + error);
  488. shutdown();
  489.  
  490. // Java is some piece of crap which doesn't allow me to set exitcode w/o
  491. // using System.exit. Maybe someday Java will be a fully functional
  492. // programming language, but I wouldn't bet my money
  493. //System.exit(2);
  494. throw new Utils.Shutdown("FUCK YOU JAVA");
  495. }
  496.  
  497. /*--------------------------------------------------------------------------*/
  498.  
  499. public void parseArgs(String[] args)
  500. {
  501. if (args.length != 1)
  502. usage();
  503.  
  504. serverName = args[0];
  505. if (serverName.length() == 0)
  506. bailout("serverName is empty");
  507. }
  508.  
  509. /*--------------------------------------------------------------------------*/
  510.  
  511. public void createInterface()
  512. {
  513. String regHost = null;
  514. int regPort = 0;
  515.  
  516. try
  517. {
  518. InputStream in = ClassLoader.getSystemResourceAsStream("registry.properties");
  519. if (in == null)
  520. bailout("Properties file doesn't exist or isn't readable");
  521. Properties props = new Properties();
  522. props.load(in);
  523. for(String prop : props.stringPropertyNames())
  524. {
  525. if (prop.equals("registry.host"))
  526. regHost = props.getProperty(prop);
  527. else if (prop.equals("registry.port"))
  528. {
  529. try
  530. {
  531. regPort = Integer.parseInt(props.getProperty(prop));
  532. if (regPort <= 0 || regPort > 65536)
  533. {
  534. err.println("Property " + prop + " must be a valid port number (1 - 65535). Skipping...");
  535. regPort = 0;
  536. }
  537. }
  538. catch(NumberFormatException e)
  539. {
  540. err.println("Property " + prop + " must be numeric. Skipping...");
  541. }
  542. }
  543. else
  544. err.println("Property " + prop + " is unknown. Skipping...");
  545. }
  546. in.close();
  547. }
  548. catch(IOException e)
  549. {
  550. bailout("Unable to read from properties file: " + e.getMessage());
  551. }
  552.  
  553. if (regHost == null || regHost.length() <= 0)
  554. bailout("Registry host is not set");
  555. if (regPort == 0)
  556. bailout("Registry port is not set");
  557.  
  558. try
  559. {
  560. registry = LocateRegistry.getRegistry(regHost, regPort);
  561. }
  562. catch(RemoteException e)
  563. {
  564. bailout("Unable to get registry: " + e.getMessage());
  565. }
  566. }
  567.  
  568. /*--------------------------------------------------------------------------*/
  569.  
  570. public void remoteLogout()
  571. {
  572. if (tInteractive != null)
  573. tInteractive.interrupt();
  574. }
  575.  
  576. /*--------------------------------------------------------------------------*/
  577.  
  578. public void shutdown()
  579. {
  580. try
  581. {
  582. if (scheduler != null)
  583. {
  584. scheduler.shutdownNow();
  585. scheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
  586. }
  587. }
  588. catch(InterruptedException e)
  589. {}
  590.  
  591. try
  592. {
  593. if (s2ciface != null)
  594. s2ciface.unexport();
  595. }
  596. catch(RemoteException e)
  597. {}
  598.  
  599. try
  600. {
  601. if (tInteractive != null)
  602. {
  603. tInteractive.interrupt();
  604. tInteractive.join();
  605. }
  606. }
  607. catch(InterruptedException e)
  608. {}
  609.  
  610. try
  611. {
  612. if (stdin != null)
  613. stdin.close();
  614. }
  615. catch(IOException e)
  616. {}
  617. }
  618.  
  619. /*--------------------------------------------------------------------------*/
  620.  
  621. public void run(String[] args)
  622. {
  623. parseArgs(args);
  624.  
  625. synchronized(mainLock)
  626. {
  627. createInterface();
  628.  
  629. try
  630. {
  631. out.println("Waiting for the server to come online...");
  632. scheduler = Executors.newScheduledThreadPool(1);
  633. ScheduledFuture<?> waitServerTimer = scheduler.scheduleAtFixedRate(
  634. new WaitForServer(), 0, 500, TimeUnit.MILLISECONDS);
  635. mainLock.wait();
  636. if (!scheduler.isShutdown() || c2siface == null)
  637. bailout(null);
  638. }
  639. catch(InterruptedException e)
  640. {
  641. /* if we get interrupted -> ignore */
  642. }
  643.  
  644. try
  645. {
  646. s2ciface = new S2CInterfaceImpl(this);
  647. }
  648. catch(RemoteException e)
  649. {
  650. bailout("Unable to export client callback");
  651. }
  652.  
  653. try
  654. {
  655. InputStream stdin = java.nio.channels.Channels.newInputStream(
  656. new FileInputStream(FileDescriptor.in).getChannel());
  657. tInteractive = new Thread(new Interactive(stdin, mainLock));
  658. tInteractive.start();
  659. }
  660. catch(NoSuchMethodException e)
  661. {
  662. bailout("Unable to setup interactive command handler");
  663. }
  664. catch(IOException e)
  665. {
  666. bailout("Unable to create object output stream: " + e.getMessage());
  667. }
  668.  
  669. out.println("Client startup successful!");
  670. try
  671. {
  672. mainLock.wait();
  673. }
  674. catch(InterruptedException e)
  675. {
  676. /* if we get interrupted -> ignore */
  677. }
  678. }
  679.  
  680. shutdown();
  681. }
  682.  
  683. /*--------------------------------------------------------------------------*/
  684.  
  685. public static void main(String[] args)
  686. {
  687. try
  688. {
  689. Client cli = new Client();
  690. cli.run(args);
  691. }
  692. catch(Utils.Shutdown e)
  693. {}
  694. }
  695. }
  696.