package com.iambookmaster.server.dao; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.jdo.PersistenceManager; import javax.jdo.Query; import javax.persistence.NonUniqueResultException; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Text; import com.iambookmaster.client.common.XMLBuilder; import com.iambookmaster.client.editor.ModelPersist; import com.iambookmaster.client.model.Model; import com.iambookmaster.server.beans.JPABook; import com.iambookmaster.server.beans.JPABookVersion; import com.iambookmaster.server.beans.JPAClob; import com.iambookmaster.server.beans.JPAUser; public class BooksDAO { private static final Logger log = Logger.getLogger(BooksDAO.class.getName()); private static final String SELECT_BOOKS="SELECT from "+JPABook.class.getName(); private static final String SELECT_BOOKS_WHERE=SELECT_BOOKS+" WHERE "; private static final String SELECT_BOOK_VERSIONS_WHERE = "SELECT from "+JPABookVersion.class.getName()+" WHERE "; private static final String SELECT_CLOB_WHERE_OWNER = "SELECT from "+JPAClob.class.getName()+" WHERE owner==_owner && version==_verson"; private static final String SELECT_CLOB_WHERE_TYPE = "SELECT from "+JPAClob.class.getName()+" WHERE owner==_owner && type==_type && version==_verson ORDER BY ordering"; @SuppressWarnings("unchecked") public List<JPABook> selectBooks(PersistenceManager em, BookCriteria criteria) { StringBuffer buffer = new StringBuffer(SELECT_BOOKS_WHERE); Query query; int par= applyCriteria(criteria,buffer,null,null); if (par>0) { Object[] params= new Object[par]; query = em.newQuery(Query.JDOQL,buffer.toString()); applyCriteria(criteria,null,query,params); return (List<JPABook>)query.executeWithArray(params); } else { return (List<JPABook>)em.newQuery(Query.JDOQL,SELECT_BOOKS).execute(); } } private int applyCriteria(BookCriteria criteria, StringBuffer buffer,Query query,Object[] params) { StringBuffer importStr=null; StringBuffer paramStr=null; if (query != null) { importStr = new StringBuffer(); paramStr = new StringBuffer(); } int param=0; if (criteria.getUser() != null) { if (buffer != null) { buffer.append("owner==userKey"); } if (query != null) { //append import and parameters importStr.append(Key.class.getName()); paramStr.append("Key userKey"); params[param]=criteria.getUser().getId(); } param++; } if (criteria.getId() != null) { if (buffer != null) { if (param>0) { buffer.append(" && "); } buffer.append("id==_id"); } if (query != null) { //append import and parameters if (importStr.indexOf(Key.class.getName())<0) { if (importStr.length()>0) { importStr.append(','); } importStr.append(Key.class.getName()); } if (paramStr.length()>0) { paramStr.append(','); } paramStr.append("Key _id"); params[param]=criteria.getId(); } param++; } if (param>0 && query != null) { importStr.insert(0,"import "); query.declareImports(importStr.toString()); query.declareParameters(paramStr.toString()); } return param; } public JPABook findBook(PersistenceManager em, BookCriteria criteria) { List<JPABook> list = selectBooks(em,criteria); if (list.size()==0) { return null; } else if (list.size()==1) { return list.get(0); } else { throw new NonUniqueResultException(criteria.toString()); } } public JPABook mergeBook(PersistenceManager em, ModelPersist model, JPABook book, boolean published) { //assign unique Key to book model.setGameKey(KeyFactory.keyToString(book.getId())); //convert Model to XML XMLBuilder builder = XMLBuilder.getStartInstance(); model.toJSON(Model.EXPORT_ALL, builder); String mod = builder.toXML(); Date update = new Date(); String modelVer = String.valueOf(model.getSettings().getGameVersion()); //existed book - add new version JPABookVersion version = new JPABookVersion(); version.setBook(book.getId()); version.setDate(update); version.setPublished(published); version.setVersions(book.getVersion()); version = em.makePersistent(version); setCLOB(em, version, JPAClob.TYPE_MODEL, mod); if (published || (book.isPublished()==false)) { //if model for publish - update everything //if mode for save - update version and unpublished book only book.setLastUpdate(update); book.setPublished(published); book.setVersion(modelVer); book.setDescription(model.getSettings().getBookDescription()); book.setName(model.getSettings().getBookTitle()); book.setAuthors(model.getSettings().getBookAuthors()); book.clearLocals(); setCLOB(em, book, JPAClob.TYPE_MODEL, mod); } log.log(Level.INFO,"Book '"+book.getName()+"' (id="+book.getId()+", extId="+book.getExternalId()+") was updated"); return book; } public JPABook mergeBook(PersistenceManager em, ModelPersist model, JPAUser user, boolean published) { JPABook book = findBook(em,user,model); if (book==null) { Date update = new Date(); String modelVer = String.valueOf(model.getSettings().getGameVersion()); //new book - add everywhere book = new JPABook(); book.setName(model.getSettings().getBookTitle()); book.setAuthors(model.getSettings().getBookAuthors()); book.setExternalId(model.getGameId()); book.setOwner(user.getId()); book.setPublished(published); book.setVersion(modelVer); book.setDescription(model.getSettings().getBookDescription()); book.setLastUpdate(update); book = em.makePersistent(book); // if (em.currentTransaction().isActive()) { // em.currentTransaction().commit(); // em.currentTransaction().begin(); // } else { // em.flush(); // } // em.refresh(book); // em.refresh(user); //assign unique Key to book model.setGameKey(KeyFactory.keyToString(book.getId())); //convert book to XML XMLBuilder builder = XMLBuilder.getStartInstance(); model.toJSON(Model.EXPORT_ALL, builder); String mod = builder.toXML(); setCLOB(em, book, JPAClob.TYPE_MODEL, mod); //add first version JPABookVersion version = new JPABookVersion(); version.setBook(book.getId()); version.setDate(update); version.setPublished(published); version.setVersions(modelVer); version = em.makePersistent(version); setCLOB(em, version, JPAClob.TYPE_MODEL, mod); log.log(Level.INFO,"Book '"+book.getName()+"' (extId="+book.getExternalId()+") was added"); return book; } else { return mergeBook(em, model, book, published); } } public JPABook findBook(PersistenceManager em, JPAUser user, ModelPersist model) { return findBook(em,user,model.getGameId()); } @SuppressWarnings("unchecked") public JPABook findBook(PersistenceManager em, JPAUser user, String externalId) { Query query = em.newQuery(Query.JDOQL,SELECT_BOOKS_WHERE+"externalId==_externalId && owner==_user"); query.declareImports("import "+Key.class.getName()); query.declareParameters("Key _user, java.lang.String _externalId"); List<JPABook> list = (List<JPABook>)query.execute(user.getId(),externalId); if (list.size()>0) { return list.get(0); } else { return null; } } // @SuppressWarnings("unchecked") // public JPABook findBook(PersistenceManager em, String id) { // Query query = em.newQuery(Query.JDOQL,SELECT_BOOKS_WHERE+"externalId==_externalId && owner==_user"); // query.declareImports("import "+Key.class.getName()); // query.declareParameters("Key _user, java.lang.String _externalId"); // List<JPABook> list = (List<JPABook>)query.execute(user.getId(),externalId); // if (list.size()>0) { // return list.get(0); // } else { // return null; // } // } public void remove(PersistenceManager em, JPABook book) { em.currentTransaction().begin(); removeAllBookVersions(em, book); em.deletePersistent(book); em.currentTransaction().commit(); em.flush(); } void removeAllBookVersions(PersistenceManager em, JPABook book) { List<JPABookVersion> list = selectBookVersions(em,book); for (JPABookVersion version : list) { em.currentTransaction().begin(); List<JPAClob> listClob = selectAllCLOBs(em,version.getId(),true); em.deletePersistentAll(listClob); em.currentTransaction().commit(); em.flush(); } em.deletePersistentAll(list); List<JPAClob> listClob = selectAllCLOBs(em,book.getId(),false); em.deletePersistentAll(listClob); } @SuppressWarnings("unchecked") public List<JPABookVersion> selectBookVersions(PersistenceManager em, JPABook book) { Query query = em.newQuery(Query.JDOQL,SELECT_BOOK_VERSIONS_WHERE+"book==_book"); query.declareImports("import "+Key.class.getName()); query.declareParameters("Key _book"); return (List<JPABookVersion>)query.execute(book.getId()); } @SuppressWarnings("unchecked") public List<JPAClob> selectAllCLOBs(PersistenceManager em, Key owner, boolean version) { Query query = em.newQuery(Query.JDOQL,SELECT_CLOB_WHERE_OWNER); query.declareImports("import "+Key.class.getName()); query.declareParameters("Key _owner,int _version"); return (List<JPAClob>)query.execute(owner,version ? 1:0); } @SuppressWarnings("unchecked") public List<JPAClob> selectCLOBs(PersistenceManager em, Key owner, int version, int type) { Query query = em.newQuery(Query.JDOQL,SELECT_CLOB_WHERE_TYPE); query.declareImports("import "+Key.class.getName()); query.declareParameters("Key _owner,int _version,int _type"); return (List<JPAClob>)query.execute(owner,version,type); } public String getCLOB(PersistenceManager em, JPABook book, int type) { switch (type) { case JPAClob.TYPE_HTML: if (book.getLocalHtml() != null) { return book.getLocalHtml(); } break; case JPAClob.TYPE_TEXT: if (book.getLocalText() != null) { return book.getLocalText(); } break; case JPAClob.TYPE_URQ: if (book.getLocalURQ() != null) { return book.getLocalURQ(); } break; default: if (book.getLocalModel() != null) { return book.getLocalModel(); } } return getCLOB(em,book.getId(),0,type); } public String getCLOB(PersistenceManager em, JPABookVersion version, int type) { switch (type) { case JPAClob.TYPE_HTML: if (version.getLocalHtml() != null) { return version.getLocalHtml(); } break; case JPAClob.TYPE_TEXT: if (version.getLocalText() != null) { return version.getLocalText(); } break; case JPAClob.TYPE_URQ: if (version.getLocalURQ() != null) { return version.getLocalURQ(); } break; default: if (version.getLocalModel() != null) { return version.getLocalModel(); } } return getCLOB(em,version.getId(),1,type); } private String getCLOB(PersistenceManager em, Key owner, int version, int type) { List<JPAClob> list = selectCLOBs(em, owner, version, type); if (list.isEmpty()) { return ""; } else { StringBuilder builder = new StringBuilder(); for (JPAClob clob : list) { builder.append(clob.getData().getValue()); } return builder.toString(); } } public void setCLOB(PersistenceManager em, JPABook book, int type,String data) { setCLOB(em,book.getId(),0,type,data); } public void setCLOB(PersistenceManager em, JPABookVersion version, int type,String data) { setCLOB(em,version.getId(),1,type,data); } private void setCLOB(PersistenceManager em, Key owner, int version, int type,String value) { List<JPAClob> list = selectCLOBs(em, owner, version, type); if (value==null || value.length()==0) { if (list.size()>0) { em.deletePersistentAll(list); } } else { int pos=0; int order=0; for (JPAClob clob : list) { if (pos>=value.length()) { em.deletePersistent(clob); } else { clob.setOrder(order++); int last = pos+JPAClob.CLOB_SIZE; if (last>value.length()) { clob.setData(new Text(value.substring(pos))); pos = value.length(); } else { clob.setData(new Text(value.substring(pos,last))); pos = last; } } } while (pos<value.length()) { //extend data int last = pos+JPAClob.CLOB_SIZE; JPAClob clob = new JPAClob(); clob.setOwner(owner); clob.setOrder(order++); clob.setType(type); clob.setVersion(version); if (last>value.length()) { clob.setData(new Text(value.substring(pos))); pos = value.length(); } else { clob.setData(new Text(value.substring(pos,last))); pos = last; } clob = em.makePersistent(clob); } } } void removeAllUserBooks(PersistenceManager em, JPAUser user) { BookCriteria criteria = new BookCriteria(); criteria.setUser(user); List<JPABook> books = selectBooks(em, criteria); for (Iterator<JPABook> iterator = books.iterator(); iterator.hasNext();) { JPABook book = iterator.next(); removeAllBookVersions(em, book); } em.deletePersistentAll(books); } public void lock(PersistenceManager em, JPABook book, boolean lock) { book.setLocked(lock); } @SuppressWarnings("unchecked") public JPABook findBook(PersistenceManager em, Key key) { Query query = em.newQuery(Query.JDOQL,SELECT_BOOKS_WHERE+"id==_id"); query.declareImports("import "+Key.class.getName()); query.declareParameters("Key _id"); List<JPABook> list = (List<JPABook>)query.execute(key); if (list.size()==1) { return list.get(0); } else if (list.size()==0) { return null; } else { throw new NonUniqueResultException(); } } @SuppressWarnings("unchecked") public JPABookVersion findBookVersion(PersistenceManager em, Key key) { Query query = em.newQuery(Query.JDOQL,SELECT_BOOK_VERSIONS_WHERE+"id==_id"); query.declareImports("import "+Key.class.getName()); query.declareParameters("Key _id"); List<JPABookVersion> list = (List<JPABookVersion>)query.execute(key); if (list.size()==1) { return list.get(0); } else if (list.size()==0) { return null; } else { throw new NonUniqueResultException(); } } }