The command pattern can be used to share common implementations of operations. This class provides an abstract implementation for common operations on books that other commands can share. Figure 15-3 shows the class diagram for a number of the use-cases.
This class implements a number of convenience methods that can be used to do the following:
Query generic fields by passing “field” and “field value,”
Return found Book instance as a subclass.
Ensure that only a single instance result is returned; else an error is thrown.
Copies of a book can be found by “back pointer” or join query; returns all copies that belong to a book.
Package com.corejdo.casestudy.usecase; import com.corejdo.casestudy.model.*; import javax.jdo.* ; import java.util.* ; /** * Basic class of all Book operations. * This class can find a single book. */ public abstract class BookOperation extends AbstractUseCase { public BookOperation() { } /** * Searches for a book that matches a string field * @param field name of field in the book class. * @param fieldValue value to match * @return a book */ protected Book findBook(String field, String fieldValue) { PersistenceManager pm = getPM(); // find the book to update. Query q = pm.newQuery(Book.class, field+" == val"); q.declareParameters("String val"); q.setCandidates(pm.getExtent(Book.class,true)); Collection result = (Collection)q.execute(fieldValue); if (result.size()<1) { throw new RuntimeException( field+" not found: "+fieldValue); } else if (result.size()>1) { throw new RuntimeException( field+" not unique: "+fieldValue); } Book book = (Book)result.iterator().next(); return book; } protected final Book findBook() { Book book = null; try { book = findBook("ISBN",this.findISBN); } catch (Exception e) { book = findBook("title",this.findTitle); } return book; } /** * Find all copies of a publication. */ protected Copy[] findCopies(Publication publication) { PersistenceManager pm = getPM(); // find all copies for a publication Query q = pm.newQuery(Copy.class, "copyOf == val"); q.declareParameters( "com.corejdo.casestudy.model.Publication val"); q.setCandidates(pm.getExtent(Copy.class,true)); Collection result = (Collection)q.execute(publication); Copy copies[] = new Copy[result.size()]; result.toArray(copies); return copies; } public void setFindTitle(String findTitle) { this.findTitle = findTitle; } public void setFindISBN(String findISBN) { this.findISBN = findISBN; } private String findTitle; private String findISBN; }
This use-case shows how to display a list of all books or books that match a specified filter.
Package com.corejdo.casestudy.usecase; import com.corejdo.casestudy.tools.CmdLine; import com.corejdo.casestudy.model.*; import javax.jdo.* ; import java.util.* ; /** * This class lists books. * */ public class ListBooks extends BookOperation { int startNumber = 0; int count = 10; List result; String filterAuthor = ""; Query query; Extent extent; /** * Sets the start offset. Since JDO does not * support a "seek" operation on * extents, we implemented our own by simply * calling iterator.next(). * @param i */ public void setStartNumber(int i) { startNumber = i; } /** * Sets the number of entries * returned by this operation. * @param i number of entries */ public void setCount(int i) { count = i; } /** * This data object is used to return the results. */ public class BookDTO { public final String title; public final String copyright; public final String ISBN; public final String author; BookDTO(Book b) { this.title = b.title; this.copyright = b.copyright; this.ISBN = b.ISBN; this.author = b.author; } public String toString() { return "Title: "+title+ " Author: "+author+ " ISBN: "+ISBN+ " ©: " "+copyright; } } /** * Iterates over elements of the book Extent. * Called by the framework's execute() method. */ protected void _execute() { result = new ArrayList(); extent = getPM().getExtent(Book.class,true); query = null; Iterator iter = filter(extent); int i = this.startNumber; while (i-- > 0 && iter.hasNext()) iter.next(); i = this.count; while (i-- > 0 && iter.hasNext()) { result.add(new BookDTO((Book)iter.next())); } if (query != null) { query.close(iter); } else { extent.close(iter); } } protected Iterator filter(Extent extent) { if (filterAuthor != null) { query = getPM().newQuery( Book.clas", "author == "al"); query.declareParameters( « "java.lang.String »a l"); query.setCandidates(exte nt); Collection result = (Collection)query.execute( filterAuthor); return result.iterator(); } else { return extent.iterator(); } } /** * Command line version. */ public static void main(String[] args) { new ListBooks().runCmdLine(args); } protected void run(String[] args) { setStartNumber(CmdLine.getIntV"r("Start"at",0)); setCount(CmdLine.getIntV"r("Co"nt",10)); execute(); System.out.print"n("Resul": "); Iterator iter = getResult(); int i = 1; while (iter.hasNext()) { System.out.print""("""i+": "+iter.next()); i++; } } /** * The resulting iterator returns BookDTO objects. */ public Iterator getResult() { return result.iterator(); } public int getCount() { return count; } public int getStartNumber() { return startNumber; } public String getFilterAuthor() { return filterAuthor; } public void setFilterAuthor(String filterAuthor) { this.filterAuthor = filterAuthor; } }
This use-case gets the detailed information about a particular book; it encapsulates a Book instance and provides an interface to get the detailed information from it without exposing the Book instance itself.
package com.corejdo.casestudy.usecase; import com.corejdo.casestudy.model.*; import com.corejdo.casestudy.tools.*; /** * This class gets detailed information about a book. * It is a good example for information hiding. * The actual book data * in the persistent model Book class * uses a User field for the * borrower information, but unauthorized users must * not be able to see detailed information about borrowers. */ public class DetailedBook extends BookOperation { public DetailedBook() { } public static void main(String[] args) { new DetailedBook().runCmdLine(args); } /** * Command line version of the book editor. */ protected void run(String[] args) { setFindISBN(CmdLine.getStringVar( " "Find I"B"","928394-234-23"44")); setFindTitle(CmdLine.getStringVar( " "Find Ti"l"","")); execute(); } /** * First, looks up a book, then sets return value * for detailed book information. * The implementation of this * method looks quite simple because JDO does a * lot of things for us, like fetching other objects * on demand. */ protected void _execute() { Book book = findBook(); this.ISBN = book.ISBN; this.author = book.author; this.title = book.title; this.published = book.published.toString(); this.firstPublished = book.firstPublished.toString(); Copy copies[] = findCopies(book); availableCopies = 0; for (int i = 0; i < copies.length; i++) { if (copies[i].borrower == null) availableCopies++; } this.isBorrowed = availableCopies > 0; } public String getAuthor() { return author; } public int getAvailableCopies() { return availableCopies; } public String getFirstPublished() { return firstPublished; } public String getISBN() { return ISBN; } public boolean isIsBorrowed() { return isBorrowed; } public String getPublished() { return published; } public String getTitle() { return title; } String ISBN; String author; String title; String published; String firstPublished; int availableCopies; boolean isBorrowed; }
Similar to the previous use-case, this one allows a particular Book instance to be modified. Again, access to the actual Book instance is encapsulated by this class.
package com.corejdo.casestudy.usecase; import com.corejdo.casestudy.model.Book; import com.corejdo.casestudy.tools.CmdLine; import javax.jd o.*; import java.uti l.*; import java.text.DateFor mat; import java.text.ParseExcept ion; /** * This class is used to change properties of books. */ public class EditBook extends BookOperation { public static void main(String[] args) { new EditBook().runCmdLine(args); } /** * Command line version of the book editor. * */ protected void run(String[] args) { setFindISBN(CmdLine.getStringVar( " "Find I"B"","928394-234-23"44")); setFindTitle(CmdLine.getStringVar( " "Find Ti"l"","")); setISBN(CmdLine.getStringVar( " "I"B"","928394-234-23"44")); setTitle(CmdLine.getStringVar( " "Ti"l"","new Ti"le")); setAuthor(CmdLine.getStringVar( " "Aut"o"","H. Bob"in")); setCopyright(CmdLine.getStringVar( " "Copyri"h"","(C) 2003 Prentice H"ll")); execute(); } /** * First, looks up a book, then updates the fields with * the values of this bean. */ protected void _execute() { Book book = findBook(); book.title = this.title; book.author = this.author; book.copyright = this.copyright; book.ISBN = this.ISBN; } public void setISBN(String ISBN) { this.ISBN = ISBN; } public void setAuthor(String author) { this.author = author; } public void setTitle(String title) { this.title = title; } public void setCopyright(String copyright) { this.copyright = copyright; } private String ISBN; private String author; private String title; private String copyright; }
This use-case simply deletes the specified Book instance. Interestingly, it doesn't try to find all the Copy instances that reference this Book and delete them, although this would be a fairly straightforward addition. Instead, the Copy instances are left as-is, now referencing a delete persistent object.
package com.corejdo.casestudy.usecase; import com.corejdo.casestudy.tools.*; import com.corejdo.casestudy.model.*; import javax.jd o.*; import java.uti l.*; public class DeleteBook extends BookOperation { public static void main(String[] args) { new DeleteBook().runCmdLine(args); } /** * Command line version. */ protected void run(String[] args) { setFindISBN(CmdLine.getStringVar( " "Find I"B"","928394-234-23"44")); setFindTitle(CmdLine.getStringVar( " "Find Ti"l"","")); execute(); } public boolean isAllowed() { return getContext().userRights.canManage(); } /** * First, looks up a book, then delete it. */ protected void _execute() { Book book = findBook(); PersistenceManager pm = getPM(); pm.deletePersistent(book); } }
This abstract class implements behavior common to borrowing or returning a book.
package com.corejdo.casestudy.usecase; import com.corejdo.casestudy.model.*; import com.corejdo.casestudy.tools.CmdLine; import javax.jd o.*; import java.uti l.*; import java.text.DateFor mat; import java.text.ParseExcept ion; /** * The common operations "f "bor"ow" * a"d "ret"rn" are implemented in this class. */ public abstract class BorrowReturn extends BookOperation { public boolean isAllowed() { if (!getContext().isLoggedIn()) return false; return getContext().userRights.canBorrow(); } /** * First, looks up a book, then checks for a copy. * If copy exists, the book is borrowed or returned. */ protected void _execute() { Book book = findBook(); process(book); } protected abstract void process(Book book); public String getLocation() { return location; } public String getReturnDate() { return returnDate; } protected String returnDate; protected String location; }
This use-case tries to find available copies of a book and allocates one to the borrower.
package com.corejdo.casestudy.usecase; ... public final long borrowTime = 1000L*60L*10L; // ten minutes protected void process(Book book) { Copy copy[] = findCopies(book); for (int i = 0; i < copy.length; i++) { Copy c = copy[i]; if (c.borrower == null) { c.borrower = getContext().user; c.borrowDate = new Date(); // now. c.returnDate = new Date( c.borrowDate.getTime()+borrowTime); this.returnDate = c.returnDate.toString(); this.location = c.location; return; } } throw new UseCaseException( " "No copy available. Please ask cle"k."); } }
This use-case tries to find the copy of a book that was previously borrowed and returns it.
package com.corejdo.casestudy.usecase; ... protected void process(Book book) { Copy copy[] = findCopies(book); for (int i = 0; i < copy.length; i++) { Copy c = copy[i]; if (c.borrower == getContext().user) { c.borrower = null; this.location = c.location; return ; } } throw new UseCaseException( " "This book is not borrowed "y "+ getContext().us"r+". Please ask cle"k."); }