Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. The ASF licenses this file to You * under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. For additional information regarding * copyright in this work, please see the NOTICE file in the top level * directory of this distribution. */ package org.apache.roller.weblogger.business.jpa; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.text.SimpleDateFormat; import java.util.TreeMap; import java.sql.Timestamp; import javax.persistence.NoResultException; import javax.persistence.Query; import org.apache.commons.collections.comparators.ReverseComparator; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.Weblogger; import org.apache.roller.weblogger.pojos.WeblogEntryComment; import org.apache.roller.weblogger.pojos.WeblogHitCount; import org.apache.roller.weblogger.pojos.WeblogReferrer; import org.apache.roller.weblogger.pojos.StatCount; import org.apache.roller.weblogger.pojos.TagStat; import org.apache.roller.weblogger.pojos.TagStatComparator; import org.apache.roller.weblogger.pojos.TagStatCountComparator; import org.apache.roller.weblogger.pojos.WeblogCategory; import org.apache.roller.weblogger.pojos.WeblogEntry; import org.apache.roller.weblogger.pojos.WeblogEntryTagAggregate; import org.apache.roller.weblogger.pojos.WeblogEntryTag; import org.apache.roller.weblogger.pojos.Weblog; import org.apache.roller.weblogger.pojos.WeblogEntryAttribute; import org.apache.roller.weblogger.pojos.StatCountCountComparator; import org.apache.roller.weblogger.pojos.User; import org.apache.roller.util.DateUtil; import org.apache.roller.weblogger.business.WeblogEntryManager; /** * JPAWeblogManagerImpl.java * * Created on May 31, 2006, 4:08 PM * */ @com.google.inject.Singleton public class JPAWeblogEntryManagerImpl implements WeblogEntryManager { protected static Log log = LogFactory.getLog(JPAWeblogEntryManagerImpl.class); private final Weblogger roller; private final JPAPersistenceStrategy strategy; // cached mapping of entryAnchors -> entryIds private Hashtable entryAnchorToIdMap = new Hashtable(); /* inline creation of reverse comparator, anonymous inner class */ private static final Comparator reverseComparator = new ReverseComparator(); private static final Comparator tagStatNameComparator = new TagStatComparator(); private static final Comparator tagStatCountReverseComparator = Collections .reverseOrder(TagStatCountComparator.getInstance()); private static final Comparator statCountCountReverseComparator = Collections .reverseOrder(StatCountCountComparator.getInstance()); @com.google.inject.Inject protected JPAWeblogEntryManagerImpl(Weblogger roller, JPAPersistenceStrategy strategy) { log.debug("Instantiating JPA Weblog Manager"); this.roller = roller; this.strategy = strategy; } /** * @inheritDoc */ public void saveWeblogCategory(WeblogCategory cat) throws WebloggerException { boolean exists = getWeblogCategory(cat.getId()) != null; if (!exists) { if (isDuplicateWeblogCategoryName(cat)) { throw new WebloggerException("Duplicate category name, cannot save category"); } // Newly added object. If it has a parent, // maintain relationship from both sides WeblogCategory parent = cat.getParent(); if (parent != null) { parent.getWeblogCategories().add(cat); } } // update weblog last modified date. date updated by saveWebsite() roller.getWeblogManager().saveWeblog(cat.getWebsite()); this.strategy.store(cat); } /** * @inheritDoc */ public void removeWeblogCategory(WeblogCategory cat) throws WebloggerException { if (cat.retrieveWeblogEntries(true).size() > 0) { throw new WebloggerException("Cannot remove category with entries"); } // remove cat this.strategy.remove(cat); //relationship management for the other side WeblogCategory parent = cat.getParent(); if (parent != null) { parent.getWeblogCategories().remove(cat); } // update website default cats if needed if (cat.getWebsite().getBloggerCategory().equals(cat)) { WeblogCategory rootCat = this.getRootWeblogCategory(cat.getWebsite()); cat.getWebsite().setBloggerCategory(rootCat); this.strategy.store(cat.getWebsite()); } if (cat.getWebsite().getDefaultCategory().equals(cat)) { WeblogCategory rootCat = this.getRootWeblogCategory(cat.getWebsite()); cat.getWebsite().setDefaultCategory(rootCat); this.strategy.store(cat.getWebsite()); } // update weblog last modified date. date updated by saveWebsite() roller.getWeblogManager().saveWeblog(cat.getWebsite()); } /** * @inheritDoc */ public void moveWeblogCategory(WeblogCategory srcCat, WeblogCategory destCat) throws WebloggerException { // TODO: this check should be made before calling this method? if (destCat.descendentOf(srcCat)) { throw new WebloggerException("ERROR cannot move parent category into it's own child"); } log.debug("Moving category " + srcCat.getPath() + " under " + destCat.getPath()); WeblogCategory oldParent = srcCat.getParent(); if (oldParent != null) { oldParent.getWeblogCategories().remove(srcCat); } srcCat.setParent(destCat); destCat.getWeblogCategories().add(srcCat); if ("/".equals(destCat.getPath())) { srcCat.setPath("/" + srcCat.getName()); } else { srcCat.setPath(destCat.getPath() + "/" + srcCat.getName()); } saveWeblogCategory(srcCat); // the main work to be done for a category move is to update the // path attribute of the category and all descendent categories updatePathTree(srcCat); } // updates the paths of all descendents of the given category private void updatePathTree(WeblogCategory cat) throws WebloggerException { log.debug("Updating path tree for category " + cat.getPath()); WeblogCategory childCat = null; Iterator childCats = cat.getWeblogCategories().iterator(); while (childCats.hasNext()) { childCat = (WeblogCategory) childCats.next(); log.debug("OLD child category path was " + childCat.getPath()); // update path and save if ("/".equals(cat.getPath())) { childCat.setPath("/" + childCat.getName()); } else { childCat.setPath(cat.getPath() + "/" + childCat.getName()); } saveWeblogCategory(childCat); log.debug("NEW child category path is " + childCat.getPath()); // then make recursive call to update this cats children updatePathTree(childCat); } } /** * @inheritDoc */ public void moveWeblogCategoryContents(WeblogCategory srcCat, WeblogCategory destCat) throws WebloggerException { // TODO: this check should be made before calling this method? if (destCat.descendentOf(srcCat)) { throw new WebloggerException("ERROR cannot move parent category into it's own child"); } // get all entries in category and subcats List results = srcCat.retrieveWeblogEntries(true); // Loop through entries in src cat, assign them to dest cat Iterator iter = results.iterator(); Weblog website = destCat.getWebsite(); while (iter.hasNext()) { WeblogEntry entry = (WeblogEntry) iter.next(); entry.setCategory(destCat); entry.setWebsite(website); this.strategy.store(entry); } // Make sure website's default and bloggerapi categories // are valid after the move if (srcCat.getWebsite().getDefaultCategory().getId().equals(srcCat.getId()) || srcCat.getWebsite().getDefaultCategory().descendentOf(srcCat)) { srcCat.getWebsite().setDefaultCategory(destCat); this.strategy.store(srcCat.getWebsite()); } if (srcCat.getWebsite().getBloggerCategory().getId().equals(srcCat.getId()) || srcCat.getWebsite().getBloggerCategory().descendentOf(srcCat)) { srcCat.getWebsite().setBloggerCategory(destCat); this.strategy.store(srcCat.getWebsite()); } } /** * @inheritDoc */ public void saveComment(WeblogEntryComment comment) throws WebloggerException { this.strategy.store(comment); // update weblog last modified date. date updated by saveWebsite() roller.getWeblogManager().saveWeblog(comment.getWeblogEntry().getWebsite()); } /** * @inheritDoc */ public void removeComment(WeblogEntryComment comment) throws WebloggerException { this.strategy.remove(comment); // update weblog last modified date. date updated by saveWebsite() roller.getWeblogManager().saveWeblog(comment.getWeblogEntry().getWebsite()); } /** * @inheritDoc */ // TODO: perhaps the createAnchor() and queuePings() items should go outside this method? public void saveWeblogEntry(WeblogEntry entry) throws WebloggerException { if (entry.getCategory() == null) { // Entry is invalid without category, so use weblog client cat WeblogCategory cat = entry.getWebsite().getBloggerCategory(); if (cat == null) { // Sill no category, so use first one found cat = (WeblogCategory) entry.getWebsite().getWeblogCategories().iterator().next(); } entry.setCategory(cat); } // Entry is invalid without local. if missing use weblog default if (entry.getLocale() == null) { entry.setLocale(entry.getWebsite().getLocale()); } if (entry.getAnchor() == null || entry.getAnchor().trim().equals("")) { entry.setAnchor(this.createAnchor(entry)); } for (Iterator it = entry.getAddedTags().iterator(); it.hasNext();) { String name = (String) it.next(); updateTagCount(name, entry.getWebsite(), 1); } for (Iterator it = entry.getRemovedTags().iterator(); it.hasNext();) { String name = (String) it.next(); updateTagCount(name, entry.getWebsite(), -1); } // if the entry was published to future, set status as SCHEDULED // we only consider an entry future published if it is scheduled // more than 1 minute into the future if ("PUBLISHED".equals(entry.getStatus()) && entry.getPubTime().after(new Date(System.currentTimeMillis() + 60000))) { entry.setStatus(WeblogEntry.SCHEDULED); } // Store value object (creates new or updates existing) entry.setUpdateTime(new Timestamp(new Date().getTime())); this.strategy.store(entry); // update weblog last modified date. date updated by saveWebsite() if (entry.isPublished()) { roller.getWeblogManager().saveWeblog(entry.getWebsite()); } if (entry.isPublished()) { // Queue applicable pings for this update. roller.getAutopingManager().queueApplicableAutoPings(entry); } } /** * @inheritDoc */ public void removeWeblogEntry(WeblogEntry entry) throws WebloggerException { Weblog weblog = entry.getWebsite(); Query q = strategy.getNamedQuery("WeblogReferrer.getByWeblogEntry"); q.setParameter(1, entry); List referers = q.getResultList(); for (Iterator iter = referers.iterator(); iter.hasNext();) { WeblogReferrer referer = (WeblogReferrer) iter.next(); this.strategy.remove(referer); } // remove comments List comments = getComments(null, // website entry, null, // search String null, // startDate null, // endDate null, // status true, // reverse chrono order (not that it matters) 0, // offset -1); // no limit Iterator commentsIT = comments.iterator(); while (commentsIT.hasNext()) { this.strategy.remove((WeblogEntryComment) commentsIT.next()); } // remove tags aggregates if (entry.getTags() != null) { for (Iterator it = entry.getTags().iterator(); it.hasNext();) { WeblogEntryTag tag = (WeblogEntryTag) it.next(); updateTagCount(tag.getName(), entry.getWebsite(), -1); it.remove(); this.strategy.remove(tag); } } // remove attributes if (entry.getEntryAttributes() != null) { for (Iterator it = entry.getEntryAttributes().iterator(); it.hasNext();) { WeblogEntryAttribute att = (WeblogEntryAttribute) it.next(); it.remove(); this.strategy.remove(att); } } // TODO: can we eliminate this unnecessary flush with OpenJPA 1.0 this.strategy.flush(); // remove entry this.strategy.remove(entry); // update weblog last modified date. date updated by saveWebsite() if (entry.isPublished()) { roller.getWeblogManager().saveWeblog(weblog); } // remove entry from cache mapping this.entryAnchorToIdMap.remove(entry.getWebsite().getHandle() + ":" + entry.getAnchor()); } public List getNextPrevEntries(WeblogEntry current, String catName, String locale, int maxEntries, boolean next) throws WebloggerException { if (current == null) { log.debug("WeblogEntry current cannot be null"); return Collections.emptyList(); } Query query = null; List results = null; WeblogCategory category = null; if (catName != null && !catName.trim().equals("/")) { category = getWeblogCategoryByPath(current.getWebsite(), null, catName); } List params = new ArrayList(); int size = 0; StringBuffer queryString = new StringBuffer(); StringBuffer whereClause = new StringBuffer(); queryString.append("SELECT e FROM WeblogEntry e WHERE "); params.add(size++, current.getWebsite()); whereClause.append("e.website = ?" + size); params.add(size++, WeblogEntry.PUBLISHED); whereClause.append(" AND e.status = ?" + size); if (next) { params.add(size++, current.getPubTime()); whereClause.append(" AND e.pubTime > ?" + size); } else { params.add(size++, current.getPubTime()); whereClause.append(" AND e.pubTime < ?" + size); } if (catName != null && !catName.trim().equals("/")) { category = getWeblogCategoryByPath(current.getWebsite(), catName); if (category != null) { params.add(size++, category); whereClause.append(" AND e.category = ?" + size); } else { throw new WebloggerException("Cannot find category: " + catName); } } if (locale != null) { params.add(size++, locale + '%'); whereClause.append(" AND e.locale like ?" + size); } if (next) { whereClause.append(" ORDER BY e.pubTime ASC"); } else { whereClause.append(" ORDER BY e.pubTime DESC"); } query = strategy.getDynamicQuery(queryString.toString() + whereClause.toString()); for (int i = 0; i < params.size(); i++) { query.setParameter(i + 1, params.get(i)); } query.setMaxResults(maxEntries); return query.getResultList(); } /** * @inheritDoc */ public WeblogCategory getRootWeblogCategory(Weblog website) throws WebloggerException { if (website == null) throw new WebloggerException("website is null"); Query q = strategy.getNamedQuery("WeblogCategory.getByWebsite&ParentNull"); q.setParameter(1, website); try { return (WeblogCategory) q.getSingleResult(); } catch (NoResultException e) { return null; } } /** * @inheritDoc */ public List getWeblogCategories(Weblog website, boolean includeRoot) throws WebloggerException { if (website == null) throw new WebloggerException("website is null"); if (includeRoot) return getWeblogCategories(website); Query q = strategy.getNamedQuery("WeblogCategory.getByWebsite&ParentNotNull"); q.setParameter(1, website); return q.getResultList(); } /** * @inheritDoc */ public List getWeblogCategories(Weblog website) throws WebloggerException { if (website == null) throw new WebloggerException("website is null"); Query q = strategy.getNamedQuery("WeblogCategory.getByWebsite"); q.setParameter(1, website); return q.getResultList(); } /** * @inheritDoc */ public List getWeblogEntries(Weblog website, User user, Date startDate, Date endDate, String catName, List tags, String status, String text, String sortby, String sortOrder, String locale, int offset, int length) throws WebloggerException { WeblogCategory cat = null; if (StringUtils.isNotEmpty(catName) && website != null) { cat = getWeblogCategoryByPath(website, catName); if (cat == null) catName = null; } if (catName != null && catName.trim().equals("/")) { catName = null; } List params = new ArrayList(); int size = 0; StringBuffer queryString = new StringBuffer(); //queryString.append("SELECT e FROM WeblogEntry e WHERE "); if (tags == null || tags.size() == 0) { queryString.append("SELECT e FROM WeblogEntry e WHERE "); } else { queryString.append("SELECT e FROM WeblogEntry e JOIN e.tags t WHERE "); queryString.append("("); for (int i = 0; i < tags.size(); i++) { if (i != 0) queryString.append(" OR "); params.add(size++, tags.get(i)); queryString.append(" t.name = ?").append(size); } queryString.append(") AND "); } if (website != null) { params.add(size++, website.getId()); queryString.append("e.website.id = ?").append(size); } else { params.add(size++, Boolean.TRUE); queryString.append("e.website.enabled = ?").append(size); } /*if (tags != null && tags.size() > 0) { // A JOIN with WeblogEntryTag in parent quert will cause a DISTINCT in SELECT clause // WeblogEntry has a clob field and many databases do not link DISTINCT for CLOB fields // Hence as a workaround using corelated EXISTS query. queryString.append(" AND EXISTS (SELECT t FROM WeblogEntryTag t WHERE " + " t.weblogEntry = e AND t.name IN ("); final String PARAM_SEPERATOR = ", "; for(int i = 0; i < tags.size(); i++) { params.add(size++, tags.get(i)); queryString.append("?").append(size).append(PARAM_SEPERATOR); } // Remove the trailing PARAM_SEPERATOR queryString.delete(queryString.length() - PARAM_SEPERATOR.length(), queryString.length()); // Close the brace FOR IN clause and EXIST clause queryString.append(" ) )"); }*/ if (user != null) { params.add(size++, user.getUserName()); queryString.append(" AND e.creatorUserName = ?").append(size); } if (startDate != null) { Timestamp start = new Timestamp(startDate.getTime()); params.add(size++, start); queryString.append(" AND e.pubTime >= ?").append(size); } if (endDate != null) { Timestamp end = new Timestamp(endDate.getTime()); params.add(size++, end); queryString.append(" AND e.pubTime <= ?").append(size); } if (cat != null && website != null) { params.add(size++, cat.getId()); queryString.append(" AND e.category.id = ?").append(size); } if (status != null) { params.add(size++, status); queryString.append(" AND e.status = ?").append(size); } if (locale != null) { params.add(size++, locale + '%'); queryString.append(" AND e.locale like ?").append(size); } if (text != null) { params.add(size++, '%' + text + '%'); queryString.append(" AND ( e.text LIKE ?").append(size); queryString.append(" OR e.summary LIKE ? ").append(size); queryString.append(" OR e.title LIKE ?").append(size); queryString.append(") "); } if (sortby != null && sortby.equals("updateTime")) { queryString.append(" ORDER BY e.updateTime "); } else { queryString.append(" ORDER BY e.pubTime "); } if (sortOrder != null && sortOrder.equals(ASCENDING)) { queryString.append("ASC "); } else { queryString.append("DESC "); } Query query = strategy.getDynamicQuery(queryString.toString()); for (int i = 0; i < params.size(); i++) { query.setParameter(i + 1, params.get(i)); } if (offset != 0) { query.setFirstResult(offset); } if (length != -1) { query.setMaxResults(length); } return query.getResultList(); } /** * @inheritDoc */ public List getWeblogEntriesPinnedToMain(Integer max) throws WebloggerException { Query query = strategy.getNamedQuery("WeblogEntry.getByPinnedToMain&statusOrderByPubTimeDesc"); query.setParameter(1, Boolean.TRUE); query.setParameter(2, WeblogEntry.PUBLISHED); if (max != null) { query.setMaxResults(max.intValue()); } return query.getResultList(); } public void removeWeblogEntryAttribute(String name, WeblogEntry entry) throws WebloggerException { // seems silly, why is this not done in WeblogEntry? for (Iterator it = entry.getEntryAttributes().iterator(); it.hasNext();) { WeblogEntryAttribute entryAttribute = (WeblogEntryAttribute) it.next(); if (entryAttribute.getName().equals(name)) { //Remove it from database this.strategy.remove(entryAttribute); //Remove it from the collection it.remove(); } } } public void removeWeblogEntryTag(String name, WeblogEntry entry) throws WebloggerException { // seems silly, why is this not done in WeblogEntry? for (Iterator it = entry.getTags().iterator(); it.hasNext();) { WeblogEntryTag tag = (WeblogEntryTag) it.next(); if (tag.getName().equals(name)) { //Call back the entity to adjust its internal state entry.onRemoveTag(name); //Remove it from database this.strategy.remove(tag); //Remove it from the collection it.remove(); } } } /** * @inheritDoc */ public WeblogEntry getWeblogEntryByAnchor(Weblog website, String anchor) throws WebloggerException { if (website == null) throw new WebloggerException("Website is null"); if (anchor == null) throw new WebloggerException("Anchor is null"); // mapping key is combo of weblog + anchor String mappingKey = website.getHandle() + ":" + anchor; // check cache first // NOTE: if we ever allow changing anchors then this needs updating if (this.entryAnchorToIdMap.containsKey(mappingKey)) { WeblogEntry entry = this.getWeblogEntry((String) this.entryAnchorToIdMap.get(mappingKey)); if (entry != null) { log.debug("entryAnchorToIdMap CACHE HIT - " + mappingKey); return entry; } else { // mapping hit with lookup miss? mapping must be old, remove it this.entryAnchorToIdMap.remove(mappingKey); } } // cache failed, do lookup Query q = strategy.getNamedQuery("WeblogEntry.getByWebsite&AnchorOrderByPubTimeDesc"); q.setParameter(1, website); q.setParameter(2, anchor); WeblogEntry entry = null; try { entry = (WeblogEntry) q.getSingleResult(); } catch (NoResultException e) { entry = null; } // add mapping to cache if (entry != null) { log.debug("entryAnchorToIdMap CACHE MISS - " + mappingKey); this.entryAnchorToIdMap.put(mappingKey, entry.getId()); } return entry; } /** * @inheritDoc */ // TODO: this method should be removed and it's functionality moved to getWeblogEntries() public List getWeblogEntries(WeblogCategory cat, boolean subcats) throws WebloggerException { List results = null; if (!subcats) { Query q = strategy.getNamedQuery("WeblogEntry.getByStatus&Category"); q.setParameter(1, WeblogEntry.PUBLISHED); q.setParameter(2, cat); results = q.getResultList(); } else { Query q = strategy.getNamedQuery("WeblogEntry.getByStatus&Category.pathLike&Website"); q.setParameter(1, WeblogEntry.PUBLISHED); q.setParameter(2, cat.getPath() + '%'); q.setParameter(3, cat.getWebsite()); results = q.getResultList(); } return results; } /** * @inheritDoc */ public String createAnchor(WeblogEntry entry) throws WebloggerException { // Check for uniqueness of anchor String base = entry.createAnchorBase(); String name = base; int count = 0; while (true) { if (count > 0) { name = base + count; } Query q = strategy.getNamedQuery("WeblogEntry.getByWebsite&Anchor"); q.setParameter(1, entry.getWebsite()); q.setParameter(2, name); List results = q.getResultList(); if (results.size() < 1) { break; } else { count++; } } return name; } /** * @inheritDoc */ public boolean isDuplicateWeblogCategoryName(WeblogCategory cat) throws WebloggerException { // ensure that no sibling categories share the same name WeblogCategory parent = cat.getParent(); if (null != parent) { return (getWeblogCategoryByPath(cat.getWebsite(), cat.getPath()) != null); } return false; } /** * @inheritDoc */ public boolean isWeblogCategoryInUse(WeblogCategory cat) throws WebloggerException { Query q = strategy.getNamedQuery("WeblogEntry.getByCategory"); q.setParameter(1, cat); int entryCount = q.getResultList().size(); if (entryCount > 0) { return true; } Iterator cats = cat.getWeblogCategories().iterator(); while (cats.hasNext()) { WeblogCategory childCat = (WeblogCategory) cats.next(); if (childCat.isInUse()) { return true; } } if (cat.getWebsite().getBloggerCategory().equals(cat)) { return true; } if (cat.getWebsite().getDefaultCategory().equals(cat)) { return true; } return false; } /** * @inheritDoc */ public List getComments(Weblog website, WeblogEntry entry, String searchString, Date startDate, Date endDate, String status, boolean reverseChrono, int offset, int length) throws WebloggerException { List params = new ArrayList(); int size = 0; StringBuffer queryString = new StringBuffer(); queryString.append("SELECT c FROM WeblogEntryComment c "); StringBuffer whereClause = new StringBuffer(); if (entry != null) { params.add(size++, entry); whereClause.append("c.weblogEntry = ?").append(size); } else if (website != null) { params.add(size++, website); whereClause.append("c.weblogEntry.website = ?").append(size); } if (searchString != null) { params.add(size++, "%" + searchString + "%"); appendConjuctionToWhereclause(whereClause, "(c.url LIKE ?").append(size).append(" OR c.content LIKE ?") .append(size).append(")"); } if (startDate != null) { Timestamp start = new Timestamp(startDate.getTime()); params.add(size++, start); appendConjuctionToWhereclause(whereClause, "c.postTime >= ?").append(size); } if (endDate != null) { Timestamp end = new Timestamp(endDate.getTime()); params.add(size++, end); appendConjuctionToWhereclause(whereClause, "c.postTime <= ?").append(size); } if (status != null) { String comparisionOperator; if ("ALL_IGNORE_SPAM".equals(status)) { // we want all comments, except spam // so that means where status != SPAM status = WeblogEntryComment.SPAM; comparisionOperator = " <> "; } else { comparisionOperator = " = "; } params.add(size++, status); appendConjuctionToWhereclause(whereClause, "c.status ").append(comparisionOperator).append('?') .append(size); } if (whereClause.length() != 0) { queryString.append(" WHERE ").append(whereClause); } if (reverseChrono) { queryString.append(" ORDER BY c.postTime DESC"); } else { queryString.append(" ORDER BY c.postTime ASC"); } Query query = strategy.getDynamicQuery(queryString.toString()); if (offset != 0) { query.setFirstResult(offset); } if (length != -1) { query.setMaxResults(length); } for (int i = 0; i < params.size(); i++) { query.setParameter(i + 1, params.get(i)); } return query.getResultList(); } /** * @inheritDoc */ public int removeMatchingComments(Weblog website, WeblogEntry entry, String searchString, Date startDate, Date endDate, String status) throws WebloggerException { // TODO dynamic bulk delete query: I'd MUCH rather use a bulk delete, // but MySQL says "General error, message from server: "You can't // specify target table 'roller_comment' for update in FROM clause" List comments = getComments(website, entry, searchString, startDate, endDate, status, true, 0, -1); int count = 0; for (Iterator it = comments.iterator(); it.hasNext();) { WeblogEntryComment comment = (WeblogEntryComment) it.next(); removeComment(comment); count++; } return count; } /** * @inheritDoc */ public WeblogCategory getWeblogCategory(String id) throws WebloggerException { return (WeblogCategory) this.strategy.load(WeblogCategory.class, id); } //--------------------------------------------- WeblogCategory Queries /** * @inheritDoc */ public WeblogCategory getWeblogCategoryByPath(Weblog website, String categoryPath) throws WebloggerException { return getWeblogCategoryByPath(website, null, categoryPath); } /** * @inheritDoc */ // TODO: ditch this method in favor of getWeblogCategoryByPath(weblog, path) public WeblogCategory getWeblogCategoryByPath(Weblog website, WeblogCategory category, String path) throws WebloggerException { if (path == null || path.trim().equals("/")) { return getRootWeblogCategory(website); } else { String catPath = path; // all cat paths must begin with a '/' if (!catPath.startsWith("/")) { catPath = "/" + catPath; } // now just do simple lookup by path Query q = strategy.getNamedQuery("WeblogCategory.getByPath&Website"); q.setParameter(1, catPath); q.setParameter(2, website); try { return (WeblogCategory) q.getSingleResult(); } catch (NoResultException e) { return null; } } } /** * @inheritDoc */ public WeblogEntryComment getComment(String id) throws WebloggerException { return (WeblogEntryComment) this.strategy.load(WeblogEntryComment.class, id); } /** * @inheritDoc */ public WeblogEntry getWeblogEntry(String id) throws WebloggerException { return (WeblogEntry) strategy.load(WeblogEntry.class, id); } /** * @inheritDoc */ public Map getWeblogEntryObjectMap(Weblog website, Date startDate, Date endDate, String catName, List tags, String status, String locale, int offset, int length) throws WebloggerException { return getWeblogEntryMap(website, startDate, endDate, catName, tags, status, false, locale, offset, length); } /** * @inheritDoc */ public Map getWeblogEntryStringMap(Weblog website, Date startDate, Date endDate, String catName, List tags, String status, String locale, int offset, int length) throws WebloggerException { return getWeblogEntryMap(website, startDate, endDate, catName, tags, status, true, locale, offset, length); } private Map getWeblogEntryMap(Weblog website, Date startDate, Date endDate, String catName, List tags, String status, boolean stringsOnly, String locale, int offset, int length) throws WebloggerException { TreeMap map = new TreeMap(reverseComparator); List entries = getWeblogEntries(website, null, // user startDate, endDate, catName, tags, status, null, // text null, // sortBy null, // sortOrder locale, offset, length); Calendar cal = Calendar.getInstance(); if (website != null) { cal.setTimeZone(website.getTimeZoneInstance()); } SimpleDateFormat formatter = DateUtil.get8charDateFormat(); for (Iterator wbItr = entries.iterator(); wbItr.hasNext();) { WeblogEntry entry = (WeblogEntry) wbItr.next(); Date sDate = DateUtil.getNoonOfDay(entry.getPubTime(), cal); if (stringsOnly) { if (map.get(sDate) == null) map.put(sDate, formatter.format(sDate)); } else { List dayEntries = (List) map.get(sDate); if (dayEntries == null) { dayEntries = new ArrayList(); map.put(sDate, dayEntries); } dayEntries.add(entry); } } return map; } /** * @inheritDoc */ public List getMostCommentedWeblogEntries(Weblog website, Date startDate, Date endDate, int offset, int length) throws WebloggerException { Query query = null; List queryResults = null; if (endDate == null) endDate = new Date(); if (website != null) { if (startDate != null) { Timestamp start = new Timestamp(startDate.getTime()); Timestamp end = new Timestamp(endDate.getTime()); query = strategy .getNamedQuery("WeblogEntryComment.getMostCommentedWeblogEntryByWebsite&EndDate&StartDate"); query.setParameter(1, website); query.setParameter(2, end); query.setParameter(3, start); } else { Timestamp end = new Timestamp(endDate.getTime()); query = strategy.getNamedQuery("WeblogEntryComment.getMostCommentedWeblogEntryByWebsite&EndDate"); query.setParameter(1, website); query.setParameter(2, end); } } else { if (startDate != null) { Timestamp start = new Timestamp(startDate.getTime()); Timestamp end = new Timestamp(endDate.getTime()); query = strategy.getNamedQuery("WeblogEntryComment.getMostCommentedWeblogEntryByEndDate&StartDate"); query.setParameter(1, end); query.setParameter(2, start); } else { Timestamp end = new Timestamp(endDate.getTime()); query = strategy.getNamedQuery("WeblogEntryComment.getMostCommentedWeblogEntryByEndDate"); query.setParameter(1, end); } } if (offset != 0) { query.setFirstResult(offset); } if (length != -1) { query.setMaxResults(length); } queryResults = query.getResultList(); List results = new ArrayList(); for (Iterator iter = queryResults.iterator(); iter.hasNext();) { Object[] row = (Object[]) iter.next(); StatCount sc = new StatCount((String) row[1], // weblog handle (String) row[2], // entry anchor (String) row[3], // entry title "statCount.weblogEntryCommentCountType", // stat desc ((Long) row[0]).longValue()); // count sc.setWeblogHandle((String) row[1]); results.add(sc); } // Original query ordered by desc count. // JPA QL doesn't allow queries to be ordered by agregates; do it in memory Collections.sort(results, statCountCountReverseComparator); return results; } /** * @inheritDoc */ public WeblogEntry getNextEntry(WeblogEntry current, String catName, String locale) throws WebloggerException { WeblogEntry entry = null; List entryList = getNextPrevEntries(current, catName, locale, 1, true); if (entryList != null && entryList.size() > 0) { entry = (WeblogEntry) entryList.get(0); } return entry; } /** * @inheritDoc */ public WeblogEntry getPreviousEntry(WeblogEntry current, String catName, String locale) throws WebloggerException { WeblogEntry entry = null; List entryList = getNextPrevEntries(current, catName, locale, 1, false); if (entryList != null && entryList.size() > 0) { entry = (WeblogEntry) entryList.get(0); } return entry; } /** * @inheritDoc */ public void release() { } /** * @inheritDoc */ public void applyCommentDefaultsToEntries(Weblog website) throws WebloggerException { if (log.isDebugEnabled()) { log.debug("applyCommentDefaults"); } // TODO: Non-standard JPA bulk update, using parameter values in set clause Query q = strategy.getNamedUpdate("WeblogEntry.updateAllowComments&CommentDaysByWebsite"); q.setParameter(1, website.getDefaultAllowComments()); q.setParameter(2, new Integer(website.getDefaultCommentDays())); q.setParameter(3, website); q.executeUpdate(); } /** * @inheritDoc */ public List getPopularTags(Weblog website, Date startDate, int offset, int limit) throws WebloggerException { Query query = null; List queryResults = null; if (website != null) { if (startDate != null) { Timestamp start = new Timestamp(startDate.getTime()); query = strategy.getNamedQuery("WeblogEntryTagAggregate.getPopularTagsByWebsite&StartDate"); query.setParameter(1, website); query.setParameter(2, start); } else { query = strategy.getNamedQuery("WeblogEntryTagAggregate.getPopularTagsByWebsite"); query.setParameter(1, website); } } else { if (startDate != null) { Timestamp start = new Timestamp(startDate.getTime()); query = strategy.getNamedQuery("WeblogEntryTagAggregate.getPopularTagsByWebsiteNull&StartDate"); query.setParameter(1, start); } else { query = strategy.getNamedQuery("WeblogEntryTagAggregate.getPopularTagsByWebsiteNull"); } } if (offset != 0) { query.setFirstResult(offset); } if (limit != -1) { query.setMaxResults(limit); } queryResults = query.getResultList(); double min = Integer.MAX_VALUE; double max = Integer.MIN_VALUE; List results = new ArrayList(limit); for (Iterator iter = queryResults.iterator(); iter.hasNext();) { Object[] row = (Object[]) iter.next(); TagStat t = new TagStat(); t.setName((String) row[0]); t.setCount(((Number) row[1]).intValue()); min = Math.min(min, t.getCount()); max = Math.max(max, t.getCount()); results.add(t); } min = Math.log(1 + min); max = Math.log(1 + max); double range = Math.max(.01, max - min) * 1.0001; for (Iterator iter = results.iterator(); iter.hasNext();) { TagStat t = (TagStat) iter.next(); t.setIntensity((int) (1 + Math.floor(5 * (Math.log(1 + t.getCount()) - min) / range))); } // sort results by name, because query had to sort by total Collections.sort(results, tagStatNameComparator); return results; } /** * @inheritDoc */ public List getTags(Weblog website, String sortBy, String startsWith, int offset, int limit) throws WebloggerException { Query query = null; List queryResults = null; boolean sortByName = sortBy == null || !sortBy.equals("count"); List params = new ArrayList(); int size = 0; StringBuffer queryString = new StringBuffer(); queryString.append("SELECT w.name, SUM(w.total) FROM WeblogEntryTagAggregate w WHERE "); if (website != null) { params.add(size++, website.getId()); queryString.append(" w.weblog.id = ?").append(size); } else { queryString.append(" w.weblog IS NULL"); } if (startsWith != null && startsWith.length() > 0) { params.add(size++, startsWith + '%'); queryString.append(" AND w.name LIKE ?" + size); } if (sortBy != null && sortBy.equals("count")) { sortBy = "w.total DESC"; } else { sortBy = "w.name"; } queryString.append(" GROUP BY w.name, w.total ORDER BY " + sortBy); query = strategy.getDynamicQuery(queryString.toString()); for (int i = 0; i < params.size(); i++) { query.setParameter(i + 1, params.get(i)); } if (offset != 0) { query.setFirstResult(offset); } if (limit != -1) { query.setMaxResults(limit); } queryResults = query.getResultList(); List results = new ArrayList(); for (Iterator iter = queryResults.iterator(); iter.hasNext();) { Object[] row = (Object[]) iter.next(); TagStat ce = new TagStat(); ce.setName((String) row[0]); // The JPA query retrieves SUM(w.total) always as long ce.setCount(((Long) row[1]).intValue()); results.add(ce); } if (sortByName) { Collections.sort(results, tagStatNameComparator); } else { Collections.sort(results, tagStatCountReverseComparator); } return results; } /** * @inheritDoc */ public boolean getTagComboExists(List tags, Weblog weblog) throws WebloggerException { if (tags == null || tags.size() == 0) { return false; } StringBuffer queryString = new StringBuffer(); queryString.append("SELECT DISTINCT w.name "); queryString.append("FROM WeblogEntryTagAggregate w WHERE w.name IN ("); //?1) AND w.weblog = ?2"); //Append tags as parameter markers to avoid potential escaping issues //The IN clause would be of form (?1, ?2, ?3, ..) ArrayList params = new ArrayList(tags.size() + 1); final String PARAM_SEPERATOR = ", "; int i; for (i = 0; i < tags.size(); i++) { queryString.append('?').append(i + 1).append(PARAM_SEPERATOR); params.add(tags.get(i)); } // Remove the trailing PARAM_SEPERATOR queryString.delete(queryString.length() - PARAM_SEPERATOR.length(), queryString.length()); // Close the brace of IN clause queryString.append(')'); if (weblog != null) { queryString.append(" AND w.weblog = ?").append(i + 1); params.add(weblog); } else { queryString.append(" AND w.weblog IS NULL"); } Query q = strategy.getDynamicQuery(queryString.toString()); for (int j = 0; j < params.size(); j++) { q.setParameter(j + 1, params.get(j)); } List results = q.getResultList(); //TODO: DatamapperPort: Since we are only interested in knowing whether //results.size() == tags.size(). This query can be optimized to just fetch COUNT //instead of objects as done currently return (results != null && results.size() == tags.size()); } /** * @inheritDoc */ public void updateTagCount(String name, Weblog website, int amount) throws WebloggerException { if (amount == 0) { throw new WebloggerException("Tag increment amount cannot be zero."); } if (website == null) { throw new WebloggerException("Website cannot be NULL."); } // The reason why add order lastUsed desc is to make sure we keep picking the most recent // one in the case where we have multiple rows (clustered environment) // eventually that second entry will have a very low total (most likely 1) and // won't matter Query weblogQuery = strategy.getNamedQuery("WeblogEntryTagAggregate.getByName&WebsiteOrderByLastUsedDesc"); weblogQuery.setParameter(1, name); weblogQuery.setParameter(2, website); WeblogEntryTagAggregate weblogTagData; try { weblogTagData = (WeblogEntryTagAggregate) weblogQuery.getSingleResult(); } catch (NoResultException e) { weblogTagData = null; } Query siteQuery = strategy .getNamedQuery("WeblogEntryTagAggregate.getByName&WebsiteNullOrderByLastUsedDesc"); siteQuery.setParameter(1, name); WeblogEntryTagAggregate siteTagData; try { siteTagData = (WeblogEntryTagAggregate) siteQuery.getSingleResult(); } catch (NoResultException e) { siteTagData = null; } Timestamp lastUsed = new Timestamp((new Date()).getTime()); // create it only if we are going to need it. if (weblogTagData == null && amount > 0) { weblogTagData = new WeblogEntryTagAggregate(null, website, name, amount); weblogTagData.setLastUsed(lastUsed); strategy.store(weblogTagData); } else if (weblogTagData != null) { weblogTagData.setTotal(weblogTagData.getTotal() + amount); weblogTagData.setLastUsed(lastUsed); strategy.store(weblogTagData); // Why use update query when only one object needs update? // Query update = strategy.getNamedUpdate( // "WeblogEntryTagAggregate.updateAddToTotalByName&Weblog"); // update.setParameter(1, new Long(amount)); // update.setParameter(2, lastUsed); // update.setParameter(3, weblogTagData.getName()); // update.setParameter(4, website); // update.executeUpdate(); } // create it only if we are going to need it. if (siteTagData == null && amount > 0) { siteTagData = new WeblogEntryTagAggregate(null, null, name, amount); siteTagData.setLastUsed(lastUsed); strategy.store(siteTagData); } else if (siteTagData != null) { siteTagData.setTotal(siteTagData.getTotal() + amount); siteTagData.setLastUsed(lastUsed); strategy.store(siteTagData); // Why use update query when only one object needs update? // Query update = strategy.getNamedUpdate( // "WeblogEntryTagAggregate.updateAddToTotalByName&WeblogNull"); // update.setParameter(1, new Long(amount)); // update.setParameter(2, siteTagData.getName()); // update.executeUpdate(); } // delete all bad counts Query removeq = strategy.getNamedUpdate("WeblogEntryTagAggregate.removeByTotalLessEqual"); removeq.setParameter(1, new Integer(0)); removeq.executeUpdate(); } /** * @inheritDoc */ public WeblogHitCount getHitCount(String id) throws WebloggerException { // do lookup return (WeblogHitCount) strategy.load(WeblogHitCount.class, id); } /** * @inheritDoc */ public WeblogHitCount getHitCountByWeblog(Weblog weblog) throws WebloggerException { Query q = strategy.getNamedQuery("WeblogHitCount.getByWeblog"); q.setParameter(1, weblog); try { return (WeblogHitCount) q.getSingleResult(); } catch (NoResultException e) { return null; } } /** * @inheritDoc */ public List getHotWeblogs(int sinceDays, int offset, int length) throws WebloggerException { // figure out start date Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); cal.add(Calendar.DATE, -1 * sinceDays); Date startDate = cal.getTime(); Query query = strategy.getNamedQuery( "WeblogHitCount.getByWeblogEnabledTrueAndActiveTrue&DailyHitsGreaterThenZero&WeblogLastModifiedGreaterOrderByDailyHitsDesc"); query.setParameter(1, startDate); // Was commented out due to https://glassfish.dev.java.net/issues/show_bug.cgi?id=2084 // TODO: determine if this is still an issue. Is it a problem with other JPA implementations? if (offset != 0) { query.setFirstResult(offset); } if (length != -1) { query.setMaxResults(length); } return query.getResultList(); } /** * @inheritDoc */ public void saveHitCount(WeblogHitCount hitCount) throws WebloggerException { this.strategy.store(hitCount); } /** * @inheritDoc */ public void removeHitCount(WeblogHitCount hitCount) throws WebloggerException { this.strategy.remove(hitCount); } /** * @inheritDoc */ public void incrementHitCount(Weblog weblog, int amount) throws WebloggerException { if (amount == 0) { throw new WebloggerException("Tag increment amount cannot be zero."); } if (weblog == null) { throw new WebloggerException("Website cannot be NULL."); } Query q = strategy.getNamedQuery("WeblogHitCount.getByWeblog"); q.setParameter(1, weblog); WeblogHitCount hitCount = null; try { hitCount = (WeblogHitCount) q.getSingleResult(); } catch (NoResultException e) { hitCount = null; } // create it if it doesn't exist if (hitCount == null && amount > 0) { hitCount = new WeblogHitCount(); hitCount.setWeblog(weblog); hitCount.setDailyHits(amount); strategy.store(hitCount); } else if (hitCount != null) { hitCount.setDailyHits(hitCount.getDailyHits() + amount); strategy.store(hitCount); } } /** * @inheritDoc */ public void resetAllHitCounts() throws WebloggerException { Query q = strategy.getNamedUpdate("WeblogHitCount.updateDailyHitCountZero"); q.executeUpdate(); } /** * @inheritDoc */ public void resetHitCount(Weblog weblog) throws WebloggerException { Query q = strategy.getNamedQuery("WeblogHitCount.getByWeblog"); q.setParameter(1, weblog); WeblogHitCount hitCount = null; try { hitCount = (WeblogHitCount) q.getSingleResult(); hitCount.setDailyHits(0); strategy.store(hitCount); } catch (NoResultException e) { // ignore: no hit count for weblog } } /** * @inheritDoc */ public long getCommentCount() throws WebloggerException { Query q = strategy.getNamedQuery("WeblogEntryComment.getCountAllDistinctByStatus"); q.setParameter(1, WeblogEntryComment.APPROVED); List results = q.getResultList(); return ((Long) results.get(0)).longValue(); } /** * @inheritDoc */ public long getCommentCount(Weblog website) throws WebloggerException { Query q = strategy.getNamedQuery("WeblogEntryComment.getCountDistinctByWebsite&Status"); q.setParameter(1, website); q.setParameter(2, WeblogEntryComment.APPROVED); List results = q.getResultList(); return ((Long) results.get(0)).longValue(); } /** * @inheritDoc */ public long getEntryCount() throws WebloggerException { Query q = strategy.getNamedQuery("WeblogEntry.getCountDistinctByStatus"); q.setParameter(1, "PUBLISHED"); List results = q.getResultList(); return ((Long) results.get(0)).longValue(); } /** * @inheritDoc */ public long getEntryCount(Weblog website) throws WebloggerException { Query q = strategy.getNamedQuery("WeblogEntry.getCountDistinctByStatus&Website"); q.setParameter(1, "PUBLISHED"); q.setParameter(2, website); List results = q.getResultList(); return ((Long) results.get(0)).longValue(); } /** * Appends given expression to given whereClause. If whereClause already * has other conditions, an " AND " is also appended before appending * the expression * @param whereClause The given where Clauuse * @param expression The given expression * @return the whereClause. */ private static StringBuffer appendConjuctionToWhereclause(StringBuffer whereClause, String expression) { if (whereClause.length() != 0 && expression.length() != 0) { whereClause.append(" AND "); } return whereClause.append(expression); } }