The following sections look at each of the persistence-capable classes. 15.2.1 Publication classThis is the base class of books and CDs in the library. Package com.corejdo.casestudy.model; import java.util.Date ; import java.util.Collection ; import javax.jdo.*; /** * This class contains generic * information common to all sorts of publications. * * <BR><FONT Color="red"><B>Persistent</B></FONT> */ public abstract class Publication { /** * The title of the publication. */ public String title; /** * Date, when the publication was published. */ public Date published; /** * Date, when the publication was first published. */ public Date firstPublished; /** * A copyright text. */ public String copyright; /** * The librarian who maintained the publication. */ public User insertedBy; public Publication() { } public String toString() { return "\n Title: "+title+ "\n Published: "+published+ "\n First published: "+firstPublished+ "\n Copyright: "+copyright; } public String toHTMLTitle(String linkPrefix) { return "<TD CLASS=pubTitle>"+title+"</TD>"; } public String toHTMLRest(String linkPrefix) { return "<TD CLASS=pubPubFirst>"+firstPublished+"</TD>"+ "<TD CLASS=pubCopyright>"+copyright+"</TD>"; } public abstract String toHTML(String linkPrefix); The getCopies() method encapsulates the JDO query to find all the Copy instances that reference a Book instance. Encapsulating a query in this way is a useful practice, although some may prefer to put these types of methods in a separate, associated class to avoid having any JDO-dependent code in their persistence classes.
15.2.2 Book classThis class represents a Book. Package com.corejdo.casestudy.model; /** * This class contains data about books, * like author, International Standard Book Number (ISBN). */ public class Book extends Publication { /** * International standard book number. */ public String ISBN; /** * Author of the book. */ public String author; public Book() { } public String toString() { return super.toString()+ "\n ISBN: "+ISBN+ "\n Author: "+author; } public String toHTML(String linkPrefix) { return "<TR CLASS=Book>"+toHTMLTitle(linkPrefix)+ "<TD CLASS=BookAuthor>"+author+"</TD>"+ "<TD CLASS=BookISBN>"+ISBN+"</TD>"+ toHTMLRest(linkPrefix)+"</TR>"; } } 15.2.3 CD classThis class represents a CD. It is more complicated than the Book class because it provides more functionality. It contains a song list that is returned in toString and toHTML . It shows basic collection operations with ArrayList . Package com.corejdo.casestudy.model; import java.util.*; /** * * <BR><FONT Color="red"><B>Persistent</B></FONT> */ public class CD extends Publication { List songs = new ArrayList(); // contains Song objects String referenceNumber; // CD reference number public CD(String referenceNumber) { this.referenceNumber = referenceNumber; } private CD() { } public void addSong(String composer, String title, int seconds) { songs.add(new Song(composer, title, seconds)); } public String songTable() { StringBuffer buf = new StringBuffer(); buf.append("<TABLE class=song>\n"); Iterator iter = songs.iterator(); while (iter.hasNext()) { Song song = (Song)iter.next(); buf.append("<TR class=song>"); buf.append("<TD class=songTitle>"); buf.append(song.title); buf.append("</TD>"); buf.append("<TD class=songComposer>"); buf.append(song.composer); buf.append("</TD>"); buf.append("<TD class=songLength>"); buf.append(song.length()); buf.append("</TD>"); buf.append("</TR>"); } buf.append("</TABLE>\n"); return buf.toString(); } public String toString() { StringBuffer buf = new StringBuffer(); buf.append("CD: "); buf.append(this.referenceNumber); buf.append(super.toString()); Iterator iter = songs.iterator(); while (iter.hasNext()) { Song song = (Song)iter.next(); buf.append(song); buf.append(", "); } return buf.toString(); } public String toHTML(String linkPrefix) { return "<TR CLASS=CD>"+toHTMLTitle(linkPrefix)+ "<TD VALIGN=TOP>"+songTable()+"</TD>"+ "<TD VALIGN=TOP></TD>"+ super.toHTMLRest(linkPrefix)+"</TR>"; } } 15.2.4 Copy classThe Copy class represents a physical copy of a particular publication. For example, a book on a library shelf is represented as an instance of the Copy class. This class has a reference back to Publication . An alternative model to this would use a collection of Copy instances in the Publication class. The benefit of this approach is that if there are a large number of instances, maintaining a collection of all of them can become problematic . Package com.corejdo.casestudy.model; import java.util.Date; /** * A copy is the instance of a publication in a * library. A copy can be located somewhere in the * library or it can be borrowed by someone. * Other states of a copy can be: missing, damaged or sold. * A copy cannot be reserved by someone, only * publications can be reserved. * * <BR><FONT Color="red"><B>Persistent</B></FONT> */ public class Copy { /** * The publication of which this is the instance of. */ public Publication copyOf; /** * This flag is set when the borrowing time is over. */ public boolean missing; /** * If the copy is damaged, this flag is set. */ public boolean damaged; /** * If someone bought the copy, this flag is set. */ public boolean sold; /** * Location of the copy in the library. */ public String location; /** * Reference to the borrower. If set, this publication instance is borrowed * by someone. */ public User borrower; /** * Date when the copy was borrowed. * Depending on the user policies of the * library. */ public Date borrowDate; /** * Date when the copy should be returned * to the library. */ public Date returnDate; public Copy(Publication copyOf) { this.copyOf = copyOf; this.location = "entry"; } private Copy() { } } 15.2.5 User classThe User class identifies a borrower and is used to define user rights or access restrictions. Package com.corejdo.casestudy.model; /** * A user object represents a user of the * library. That may be either a * borrower or a librarian (or both). * * <BR><FONT Color="red"><B>Persistent</B></FONT> */ public class User { public User() { } /** * Name of user. */ private String firstName; /** * Name of user. */ private String lastName; /** * Cleartext password. */ private String password; /** * Internally used user id. */ private String id; /** * The rights of the user. */ private Rights rights; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getLastName() { return lastName; } public void setPassword(String password) { this.password = password; } public String getId() { return id; } public void setId(String id) { this.id = id; } public boolean checkPassword(String pw) { return pw.equals(password); } public Rights getRights() { if (rights == null) rights = new Rights(); return rights; } public String toString() { return firstName+" "+lastName; } } 15.2.6 Right and Rights classesThe Right class is an example of the Enumeration Pattern. It implements a tri-state enumeration ( ALLOWED , DENIED , DEFAULT ). The Right class itself cannot be made persistent, because the JDO Implementation will not be able to return the constant references for ALLOWED , DENIED and DEFAULT , which are static objects. Package com.corejdo.casestudy.model; /** * A transient tri-state value. * It may be set either to allow, deny or default. * <BR><B>not persistent</B> */ public final class Right { boolean isAllowed(Right def) { if (this != DEFAULT) return this == ALLOWED; if (def == null def == DEFAULT) return false; return def == ALLOWED; } boolean isDenied(Right def) { if (this != DEFAULT) return this == DENIED; if (def == null def == DEFAULT) return true; return def == DENIED; } public static Right DENIED = new Right(-1); public static Right ALLOWED = new Right(1); public static Right DEFAULT = new Right(0); private int code; private Right(int code) { this.code = code; } } The Rights class maps the static Right objects to appropriate integer values: package com.corejdo.casestudy.model; import javax.jdo.InstanceCallbacks; import javax.jdo.JDOHelper; /** * Defines access restrictions for users and * user groups of the library. * * This is an example for a type mapping: * Instead of defining another persistent * class Right, the tri-state values * (allow, deny, default) are coded into plain * integer values, (+1, -1, 0 respectively). * For a better programming model, the * Right class is exposed to the application, * but stored internally as integer * values. * * <BR><FONT Color="red"><B>Persistent</B></FONT> */ public class Rights implements Cloneable { /** * The right to add, remove or * modify users of the library. */ private int _modifyUser; /** * The right to borrow something. */ private int _borrow; /** * The right to manage publications. */ private int _manage; /** * The constructor creates a default Rights object. * (An unauthorized user.) */ public Rights() { } private int rightToInt(Right r) { if (r == Right.ALLOWED) return 1; if (r == Right.DEFAULT) return 0; if (r == Right.DENIED) return 1; throw new RuntimeException("Illegal value: "+r); } private Right intToRight(int i) { if (i == 1) return Right.ALLOWED; if (i == 0) return Right.DEFAULT; if (i == -1) return Right.DENIED; throw new RuntimeException("Illegal value: "+i); } public boolean canBorrow() { return intToRight(_borrow).isAllowed(Right.ALLOWED); } public boolean canManage() { return intToRight(_manage).isAllowed(Right.DENIED); } public boolean canModifyUser() { return intToRight(_modifyUser).isAllowed(Right.DENIED); } public void setBorrow(Right right) { _borrow = rightToInt(right); } public void setManage(Right right) { _manage = rightToInt(right); } public void setModifyUser(Right right) { _modifyUser = rightToInt(right); } public Rights copy() { try { int k = this._borrow; Rights r = (Rights)this.clone(); return r; } catch (CloneNotSupportedException c) { throw new RuntimeException("Unexpected: "+c); } } public String toString() { String res = (canBorrow() ? "borrow" : "noborrow")+ (canManage() ? ",manage" : ",nomanage")+ (canModifyUser() ? ",modify" : ",nomodify"); return res; } } |