001package edu.pdx.cs410J.family; 002 003import java.io.File; 004import java.io.IOException; 005import java.io.PrintStream; 006import java.rmi.Naming; 007import java.rmi.RMISecurityManager; 008import java.rmi.RemoteException; 009import java.rmi.server.UnicastRemoteObject; 010import java.util.*; 011 012/** 013 * This class is a remote family tree whose contents are read from and 014 * saved to an XML file. It extends <code>UnicastRemoteObject</code> 015 * because it is going to be bound into the RMI registry. 016 */ 017@SuppressWarnings("serial") 018public class XmlRemoteFamilyTree extends UnicastRemoteObject 019 implements RemoteFamilyTree { 020 021 /** The underlying family tree whose contents this remote family 022 * tree serves up. */ 023 private transient FamilyTree tree; 024 025 /** The XML file that serves as the source of this family tree */ 026 private transient File xmlFile; 027 028 /** The highest id in the family tree */ 029 private int highestId; 030 031 /** Maps ids to their RemotePerson. This way there is always a 032 * one-to-one correspondence between a Person and a RemotePerson */ 033 private Map<Integer, RemotePerson> remotePersons = new TreeMap<Integer, RemotePerson>(); 034 035 /** Maps a Long that represents the husband and wife to their 036 * RemoteMarriage */ 037 private Map<Long, RemoteMarriage> remoteMarriages = new TreeMap<Long, RemoteMarriage>(); 038 039 /////////////////////// Constructors /////////////////////// 040 041 /** 042 * Creates a new <code>XmlRemoteFamilyTree</code> that gets its data 043 * from a given XML file. 044 * 045 * @throws FamilyTreeException 046 * A problem occurred while parsing the XML file 047 */ 048 public XmlRemoteFamilyTree(File xmlFile) 049 throws RemoteException, IOException, FamilyTreeException { 050 super(); // Register this object with RMI runtime 051 052 if (xmlFile.exists()) { 053 out.println("Reading Family Tree from " + xmlFile); 054 XmlParser parser = new XmlParser(xmlFile); 055 this.tree = parser.parse(); 056 057 // Compute the highest id of any person 058 Iterator iter = this.tree.getPeople().iterator(); 059 while (iter.hasNext()) { 060 Person person = (Person) iter.next(); 061 if (person.getId() > this.highestId) { 062 this.highestId = person.getId(); 063 } 064 } 065 066 } else { 067 this.tree = new FamilyTree(); 068 this.highestId = 0; 069 } 070 071 this.xmlFile = xmlFile; 072 } 073 074 ////////////////////// Instance Methods ////////////////////// 075 076 /** 077 * Helper method that manages the cache of RemotePersons 078 */ 079 private RemotePerson getRemotePerson(Person person) 080 throws RemoteException { 081 082 if (person == null) { 083 return null; 084 } 085 086 int id = person.getId(); 087 088 RemotePerson rPerson = 089 (RemotePerson) this.remotePersons.get(new Integer(id)); 090 if (rPerson == null) { 091 // Is there a person with that id 092 if (!this.tree.containsPerson(id)) { 093 return null; 094 } 095 096 rPerson = new RemotePersonImpl(this, person); 097 this.remotePersons.put(new Integer(person.getId()), rPerson); 098 } 099 100 return rPerson; 101 } 102 103 public RemotePerson createPerson(Person.Gender gender) throws RemoteException { 104 Person person = new Person(++this.highestId, gender); 105 this.tree.addPerson(person); 106 return getRemotePerson(person); 107 } 108 109 public RemotePerson getPerson(int id) throws RemoteException { 110 return getRemotePerson(this.tree.getPerson(id)); 111 } 112 113 public RemotePerson getPerson(String firstName, String lastName) 114 throws RemoteException { 115 Person person = null; 116 117 Iterator people = this.tree.getPeople().iterator(); 118 while (people.hasNext()) { 119 Person p = (Person) people.next(); 120 if (p.getFirstName().equals(firstName) && 121 p.getLastName().equals(lastName)) { 122 if (person == null) { 123 person = p; 124 125 } else { 126 String s = "Multiple people named \"" + firstName + " " + 127 lastName + " exist: " + p + " AND " + person; 128 throw new IllegalArgumentException(s); 129 } 130 } 131 } 132 133 if (person == null) { 134 return null; 135 136 } else { 137 return getPerson(person.getId()); 138 } 139 } 140 141 public RemoteMarriage getMarriage(int husbandId, int wifeId) 142 throws RemoteException { 143 144 // Make sure that both the husband and the wife exist 145 if (!this.tree.containsPerson(husbandId)) { 146 String s = "Could not find person with id " + husbandId; 147 throw new IllegalArgumentException(s); 148 149 } else if (!this.tree.containsPerson(wifeId)) { 150 String s = "Could not find person with id " + wifeId; 151 throw new IllegalArgumentException(s); 152 } 153 154 Person husband = this.tree.getPerson(husbandId); 155 Iterator marriages = husband.getMarriages().iterator(); 156 while (marriages.hasNext()) { 157 Marriage marriage = (Marriage) marriages.next(); 158 if (marriage.getWife().getId() == wifeId) { 159 return getRemoteMarriage(marriage); 160 } 161 } 162 163 return null; 164 } 165 166 /** 167 * Helper method that maintains the map of RemoteMarriages 168 */ 169 private RemoteMarriage getRemoteMarriage(Marriage marriage) 170 throws RemoteException { 171 172 long key = (((long) marriage.getHusband().getId()) << 32) | 173 ((long) marriage.getWife().getId()); 174 175 RemoteMarriage rMarriage = 176 (RemoteMarriage) this.remoteMarriages.get(new Long(key)); 177 if (rMarriage == null) { 178 rMarriage = new RemoteMarriageImpl(marriage); 179 this.remoteMarriages.put(new Long(key), rMarriage); 180 } 181 182 return rMarriage; 183 } 184 185 public RemoteMarriage createMarriage(int husbandId, int wifeId) 186 throws RemoteException { 187 188 // Make sure that both the husband and the wife exist 189 if (!this.tree.containsPerson(husbandId)) { 190 String s = "Could not find person with id " + husbandId; 191 throw new IllegalArgumentException(s); 192 193 } else if (!this.tree.containsPerson(wifeId)) { 194 String s = "Could not find person with id " + wifeId; 195 throw new IllegalArgumentException(s); 196 } 197 198 Person husband = this.tree.getPerson(husbandId); 199 Person wife = this.tree.getPerson(wifeId); 200 201 Marriage marriage = new Marriage(husband, wife); 202 husband.addMarriage(marriage); 203 wife.addMarriage(marriage); 204 return getRemoteMarriage(marriage); 205 } 206 207 public Collection<RemotePerson> getLiving() throws RemoteException { 208 Collection<RemotePerson> living = new ArrayList<RemotePerson>(); 209 210 Iterator people = this.tree.getPeople().iterator(); 211 while (people.hasNext()) { 212 Person person = (Person) people.next(); 213 if (person.getDateOfBirth() != null && 214 person.getDateOfDeath() == null) { 215 living.add(getRemotePerson(person)); 216 } 217 } 218 219 return living; 220 } 221 222 public Collection<RemotePerson> getLiving(Date date) throws RemoteException { 223 Collection<RemotePerson> alive = new ArrayList<RemotePerson>(); 224 225 Iterator people = this.tree.getPeople().iterator(); 226 while (people.hasNext()) { 227 Person person = (Person) people.next(); 228 Date dob = person.getDateOfBirth(); 229 Date dod = person.getDateOfDeath(); 230 231 if (dob != null && date.before(dob)) { 232 continue; 233 } 234 235 if (dod != null && date.after(dod)) { 236 continue; 237 } 238 239 if (dob == null && dod == null) { 240 continue; 241 } 242 243 alive.add(getRemotePerson(person)); 244 } 245 246 return alive; 247 } 248 249 public void shutdown() throws IOException, RemoteException { 250 // Write the contents of this family tree back to the XML file 251 out.println("Writing family tree to " + this.xmlFile.getPath()); 252 XmlDumper dumper = new XmlDumper(this.xmlFile.getPath()); 253 dumper.dump(this.tree); 254 255 UnicastRemoteObject.unexportObject(this, false /* force */); 256 } 257 258 ////////////////////////// Main Program ////////////////////////// 259 260 private static PrintStream out = System.out; 261 private static PrintStream err = System.err; 262 263 /** 264 * Prints usage information about this program 265 */ 266 private static void usage(String s) { 267 err.println("\n** " + s + "\n"); 268 err.println("usage: java XmlRemoteFamilyTree [-start xmlFile " + 269 "| -stop] familyName host port"); 270 err.println(""); 271 err.println("This program creates a new XmlRemoteFamilyTree " + 272 "that reads its contents from a given XML file and " + 273 "binds it into the RMI registry. If the XML file " + 274 "doesn't exist, a new one will be created"); 275 System.exit(1); 276 } 277 278 public static void main(String[] args) { 279 String xmlFileName = null; 280 String familyName = null; 281 String host = null; 282 int port = -1; 283 String command = null; 284 285 for (int i = 0; i < args.length; i++) { 286 if (args[i].equals("-start")) { 287 if (++i >= args.length) { 288 usage("Missing XML file"); 289 } 290 xmlFileName = args[i]; 291 292 command = "START"; 293 294 } else if (args[i].equals("-stop")) { 295 command = "STOP"; 296 297 } else if (familyName == null) { 298 familyName = args[i]; 299 300 } else if (host == null) { 301 host = args[i]; 302 303 } else if (port == -1) { 304 try { 305 port = Integer.parseInt(args[i]); 306 307 } catch (NumberFormatException ex) { 308 usage("Invalid port: " + args[i]); 309 } 310 311 } else { 312 usage("Spurious command line: " + args[i]); 313 } 314 } 315 316 if (command == null) { 317 usage("Missing command"); 318 } 319 320 if (familyName == null) { 321 usage("Missing family name"); 322 } 323 324 if (host == null) { 325 usage("Missing host name"); 326 } 327 328 if (port == -1) { 329 usage("Missing port number"); 330 } 331 332 if (command.equals("START")) { 333 // Install an RMISecurityManager, if there is not a 334 // SecurityManager already installed 335 if (System.getSecurityManager() == null) { 336 System.setSecurityManager(new RMISecurityManager()); 337 } 338 339 String name = "rmi://" + host + ":" + port + "/" + familyName; 340 341 try { 342 XmlRemoteFamilyTree tree = 343 new XmlRemoteFamilyTree(new File(xmlFileName)); 344 Naming.rebind(name, tree); 345 out.println("Successfully bound XmlRemoteFamilyTree"); 346 347 } catch (Exception ex) { 348 ex.printStackTrace(System.err); 349 } 350 351 352 } else if (command.equals("STOP")) { 353 // Install an RMISecurityManager, if there is not a 354 // SecurityManager already installed 355 if (System.getSecurityManager() == null) { 356 System.setSecurityManager(new RMISecurityManager()); 357 } 358 359 String name = "rmi://" + host + ":" + port + "/" + familyName; 360 361 try { 362 RemoteFamilyTree tree = 363 (RemoteFamilyTree) Naming.lookup(name); 364 tree.shutdown(); 365 Naming.unbind(name); 366 367 } catch (Exception ex) { 368 ex.printStackTrace(err); 369 } 370 371 372 } else { 373 String s = "Unknown command: " + command; 374 throw new IllegalStateException(s); 375 } 376 } 377 378}