blob: 8cb1f96727efbfad97922e4c9c215a9000b14cf3 [file] [log] [blame]
/*
* 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 org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.business.pings.AutoPingManager;
import org.apache.roller.weblogger.business.pings.PingTargetManager;
import org.apache.roller.weblogger.config.WebloggerConfig;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.roller.weblogger.business.MediaFileManager;
import org.apache.roller.weblogger.business.UserManager;
import org.apache.roller.weblogger.business.WeblogEntryManager;
import org.apache.roller.weblogger.business.WeblogManager;
import org.apache.roller.weblogger.business.Weblogger;
import org.apache.roller.weblogger.business.WebloggerFactory;
import org.apache.roller.weblogger.pojos.AutoPing;
import org.apache.roller.weblogger.pojos.CustomTemplateRendition;
import org.apache.roller.weblogger.pojos.PingQueueEntry;
import org.apache.roller.weblogger.pojos.PingTarget;
import org.apache.roller.weblogger.pojos.StatCount;
import org.apache.roller.weblogger.pojos.StatCountCountComparator;
import org.apache.roller.weblogger.pojos.TagStat;
import org.apache.roller.weblogger.pojos.ThemeTemplate.ComponentType;
import org.apache.roller.weblogger.pojos.User;
import org.apache.roller.weblogger.pojos.Weblog;
import org.apache.roller.weblogger.pojos.WeblogBookmark;
import org.apache.roller.weblogger.pojos.WeblogBookmarkFolder;
import org.apache.roller.weblogger.pojos.WeblogCategory;
import org.apache.roller.weblogger.pojos.WeblogEntry;
import org.apache.roller.weblogger.pojos.WeblogEntryTag;
import org.apache.roller.weblogger.pojos.WeblogEntryTagAggregate;
import org.apache.roller.weblogger.pojos.WeblogPermission;
import org.apache.roller.weblogger.pojos.WeblogTemplate;
/*
* JPAWeblogManagerImpl.java
* Created on May 31, 2006, 4:08 PM
*/
@com.google.inject.Singleton
public class JPAWeblogManagerImpl implements WeblogManager {
private static final Log log = LogFactory.getLog(JPAWeblogManagerImpl.class);
private static final Comparator<StatCount> STAT_COUNT_COUNT_REVERSE_COMPARATOR =
Collections.reverseOrder(StatCountCountComparator.getInstance());
private final Weblogger roller;
private final JPAPersistenceStrategy strategy;
// cached mapping of weblogHandles -> weblogIds
private final Map<String, String> weblogHandleToIdMap = Collections.synchronizedMap(new HashMap<>());
@com.google.inject.Inject
protected JPAWeblogManagerImpl(Weblogger roller, JPAPersistenceStrategy strat) {
log.debug("Instantiating JPA Weblog Manager");
this.roller = roller;
this.strategy = strat;
}
@Override
public void release() {}
/**
* Update existing weblog.
*/
@Override
public void saveWeblog(Weblog weblog) throws WebloggerException {
weblog.setLastModified(new java.util.Date());
strategy.store(weblog);
}
@Override
public void removeWeblog(Weblog weblog) throws WebloggerException {
// remove contents first, then remove weblog
this.removeWeblogContents(weblog);
this.strategy.remove(weblog);
// remove entry from cache mapping
this.weblogHandleToIdMap.remove(weblog.getHandle());
}
/**
* convenience method for removing contents of a weblog.
* TODO BACKEND: use manager methods instead of queries here
*/
private void removeWeblogContents(Weblog weblog)
throws WebloggerException {
UserManager umgr = roller.getUserManager();
WeblogEntryManager emgr = roller.getWeblogEntryManager();
// remove tags
TypedQuery<WeblogEntryTag> tagQuery = strategy.getNamedQuery("WeblogEntryTag.getByWeblog",
WeblogEntryTag.class);
tagQuery.setParameter(1, weblog);
List<WeblogEntryTag> results = tagQuery.getResultList();
for (WeblogEntryTag tagData : results) {
if (tagData.getWeblogEntry() != null) {
tagData.getWeblogEntry().getTags().remove(tagData);
}
this.strategy.remove(tagData);
}
// remove site tag aggregates
List<TagStat> tags = emgr.getTags(weblog, null, null, 0, -1);
updateTagAggregates(tags);
// delete all weblog tag aggregates
Query removeAggs= strategy.getNamedUpdate(
"WeblogEntryTagAggregate.removeByWeblog");
removeAggs.setParameter(1, weblog);
removeAggs.executeUpdate();
// delete all bad counts
Query removeCounts = strategy.getNamedUpdate(
"WeblogEntryTagAggregate.removeByTotalLessEqual");
removeCounts.setParameter(1, 0);
removeCounts.executeUpdate();
// Remove the weblog's ping queue entries
TypedQuery<PingQueueEntry> q = strategy.getNamedQuery("PingQueueEntry.getByWebsite", PingQueueEntry.class);
q.setParameter(1, weblog);
List<PingQueueEntry> queueEntries = q.getResultList();
for (Object obj : queueEntries) {
this.strategy.remove(obj);
}
// Remove the weblog's auto ping configurations
AutoPingManager autoPingMgr = roller.getAutopingManager();
List<AutoPing> autopings = autoPingMgr.getAutoPingsByWebsite(weblog);
for (AutoPing autoPing : autopings) {
this.strategy.remove(autoPing);
}
// remove associated templates
TypedQuery<WeblogTemplate> templateQuery = strategy.getNamedQuery("WeblogTemplate.getByWeblog",
WeblogTemplate.class);
templateQuery.setParameter(1, weblog);
List<WeblogTemplate> templates = templateQuery.getResultList();
for (WeblogTemplate template : templates) {
this.strategy.remove(template);
}
// remove folders (including bookmarks)
TypedQuery<WeblogBookmarkFolder> folderQuery = strategy.getNamedQuery("WeblogBookmarkFolder.getByWebsite",
WeblogBookmarkFolder.class);
folderQuery.setParameter(1, weblog);
List<WeblogBookmarkFolder> folders = folderQuery.getResultList();
for (WeblogBookmarkFolder wbf : folders) {
this.strategy.remove(wbf);
}
// remove mediafile metadata
// remove uploaded files
MediaFileManager mfmgr = WebloggerFactory.getWeblogger().getMediaFileManager();
mfmgr.removeAllFiles(weblog);
//List<MediaFileDirectory> dirs = mmgr.getMediaFileDirectories(weblog);
//for (MediaFileDirectory dir : dirs) {
//this.strategy.remove(dir);
//}
this.strategy.flush();
// remove entries
TypedQuery<WeblogEntry> refQuery = strategy.getNamedQuery("WeblogEntry.getByWebsite", WeblogEntry.class);
refQuery.setParameter(1, weblog);
List<WeblogEntry> entries = refQuery.getResultList();
for (WeblogEntry entry : entries) {
emgr.removeWeblogEntry(entry);
}
this.strategy.flush();
// delete all weblog categories
Query removeCategories= strategy.getNamedUpdate("WeblogCategory.removeByWeblog");
removeCategories.setParameter(1, weblog);
removeCategories.executeUpdate();
// remove permissions
for (WeblogPermission perm : umgr.getWeblogPermissions(weblog)) {
umgr.revokeWeblogPermission(perm.getWeblog(), perm.getUser(), WeblogPermission.ALL_ACTIONS);
}
// flush the changes before returning. This is required as there is a
// circular dependency between WeblogCategory and Weblog
this.strategy.flush();
}
protected void updateTagAggregates(List<TagStat> tags) throws WebloggerException {
for (TagStat stat : tags) {
TypedQuery<WeblogEntryTagAggregate> query = strategy.getNamedQueryCommitFirst(
"WeblogEntryTagAggregate.getByName&WebsiteNullOrderByLastUsedDesc", WeblogEntryTagAggregate.class);
query.setParameter(1, stat.getName());
try {
WeblogEntryTagAggregate agg = query.getSingleResult();
agg.setTotal(agg.getTotal() - stat.getCount());
} catch (NoResultException ignored) {
// nothing to update
}
}
}
/**
* @see org.apache.roller.weblogger.business.WeblogManager#saveTemplate(WeblogTemplate)
*/
@Override
public void saveTemplate(WeblogTemplate template) throws WebloggerException {
this.strategy.store(template);
// update weblog last modified date. date updated by saveWeblog()
roller.getWeblogManager().saveWeblog(template.getWeblog());
}
@Override
public void saveTemplateRendition(CustomTemplateRendition rendition) throws WebloggerException {
this.strategy.store(rendition);
// update weblog last modified date. date updated by saveWeblog()
roller.getWeblogManager().saveWeblog(rendition.getWeblogTemplate().getWeblog());
}
@Override
public void removeTemplate(WeblogTemplate template) throws WebloggerException {
this.strategy.remove(template);
// update weblog last modified date. date updated by saveWeblog()
roller.getWeblogManager().saveWeblog(template.getWeblog());
}
@Override
public void addWeblog(Weblog newWeblog) throws WebloggerException {
this.strategy.store(newWeblog);
this.strategy.flush();
this.addWeblogContents(newWeblog);
}
private void addWeblogContents(Weblog newWeblog)
throws WebloggerException {
// grant weblog creator ADMIN permission
List<String> actions = new ArrayList<>();
actions.add(WeblogPermission.ADMIN);
roller.getUserManager().grantWeblogPermission(
newWeblog, newWeblog.getCreator(), actions);
String cats = WebloggerConfig.getProperty("newuser.categories");
WeblogCategory firstCat = null;
if (cats != null) {
String[] splitcats = cats.split(",");
for (String split : splitcats) {
if (split.isBlank()) {
continue;
}
WeblogCategory c = new WeblogCategory(
newWeblog,
split,
null,
null );
if (firstCat == null) {
firstCat = c;
}
this.strategy.store(c);
}
}
// Use first category as default for Blogger API
if (firstCat != null) {
newWeblog.setBloggerCategory(firstCat);
}
this.strategy.store(newWeblog);
// add default bookmarks
WeblogBookmarkFolder defaultFolder = new WeblogBookmarkFolder(
"default", newWeblog);
this.strategy.store(defaultFolder);
String blogroll = WebloggerConfig.getProperty("newuser.blogroll");
if (blogroll != null) {
String[] splitroll = blogroll.split(",");
for (String splitItem : splitroll) {
String[] rollitems = splitItem.split("\\|");
if (rollitems.length > 1) {
WeblogBookmark b = new WeblogBookmark(
defaultFolder,
rollitems[0],
"",
rollitems[1].trim(),
null,
null);
this.strategy.store(b);
}
}
}
roller.getMediaFileManager().createDefaultMediaFileDirectory(newWeblog);
// flush so that all data up to this point can be available in db
this.strategy.flush();
// add any auto enabled ping targets
PingTargetManager pingTargetMgr = roller.getPingTargetManager();
AutoPingManager autoPingMgr = roller.getAutopingManager();
for (PingTarget pingTarget : pingTargetMgr.getCommonPingTargets()) {
if(pingTarget.isAutoEnabled()) {
AutoPing autoPing = new AutoPing(
null, pingTarget, newWeblog);
autoPingMgr.saveAutoPing(autoPing);
}
}
}
@Override
public Weblog getWeblog(String id) throws WebloggerException {
return (Weblog) this.strategy.load(Weblog.class, id);
}
@Override
public Weblog getWeblogByHandle(String handle) throws WebloggerException {
return getWeblogByHandle(handle, Boolean.TRUE);
}
/**
* Return weblog specified by handle.
*/
@Override
public Weblog getWeblogByHandle(String handle, Boolean visible) throws WebloggerException {
if (handle == null) {
throw new WebloggerException("Handle cannot be null");
} else if (!isAlphanumeric(handle)) {
throw new WebloggerException("Invalid handle: '"+handle+"'");
}
// check cache first
// NOTE: if we ever allow changing handles then this needs updating
String blogID = this.weblogHandleToIdMap.get(handle);
if(blogID != null) {
Weblog weblog = this.getWeblog(blogID);
if (weblog != null) {
// only return weblog if enabled status matches
if(visible == null || visible.equals(weblog.getVisible())) {
log.debug("weblogHandleToId CACHE HIT - "+handle);
return weblog;
}
} else {
// mapping hit with lookup miss? mapping must be old, remove it
this.weblogHandleToIdMap.remove(handle);
}
}
TypedQuery<Weblog> query = strategy.getNamedQuery("Weblog.getByHandle", Weblog.class);
query.setParameter(1, handle);
Weblog weblog;
try {
weblog = query.getSingleResult();
} catch (NoResultException e) {
weblog = null;
}
// add mapping to cache
if(weblog != null) {
log.debug("weblogHandleToId CACHE MISS - "+handle);
this.weblogHandleToIdMap.put(weblog.getHandle(), weblog.getId());
}
if(weblog != null &&
(visible == null || visible.equals(weblog.getVisible()))) {
return weblog;
} else {
return null;
}
}
/**
* Get weblogs of a user
*/
@Override
public List<Weblog> getWeblogs(
Boolean enabled, Boolean active,
Date startDate, Date endDate, int offset, int length) throws WebloggerException {
//if (endDate == null) endDate = new Date();
List<Object> params = new ArrayList<>();
int size = 0;
String queryString;
StringBuilder whereClause = new StringBuilder();
queryString = "SELECT w FROM Weblog w WHERE ";
if (startDate != null) {
Timestamp start = new Timestamp(startDate.getTime());
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
params.add(size++, start);
whereClause.append(" w.dateCreated > ?").append(size);
}
if (endDate != null) {
Timestamp end = new Timestamp(endDate.getTime());
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
params.add(size++, end);
whereClause.append(" w.dateCreated < ?").append(size);
}
if (enabled != null) {
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
params.add(size++, enabled);
whereClause.append(" w.visible = ?").append(size);
}
if (active != null) {
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
params.add(size++, active);
whereClause.append(" w.active = ?").append(size);
}
whereClause.append(" ORDER BY w.dateCreated DESC");
TypedQuery<Weblog> query = strategy.getDynamicQuery(queryString + whereClause.toString(), Weblog.class);
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();
}
@Override
public List<Weblog> getUserWeblogs(User user, boolean enabledOnly) throws WebloggerException {
List<Weblog> weblogs = new ArrayList<>();
if (user == null) {
return weblogs;
}
List<WeblogPermission> perms = roller.getUserManager().getWeblogPermissions(user);
for (WeblogPermission perm : perms) {
Weblog weblog = perm.getWeblog();
if ((!enabledOnly || weblog.getVisible()) && BooleanUtils.isTrue(weblog.getActive())) {
weblogs.add(weblog);
}
}
return weblogs;
}
@Override
public List<User> getWeblogUsers(Weblog weblog, boolean enabledOnly) throws WebloggerException {
List<User> users = new ArrayList<>();
List<WeblogPermission> perms = roller.getUserManager().getWeblogPermissions(weblog);
for (WeblogPermission perm : perms) {
User user = perm.getUser();
if (user == null) {
log.error("ERROR user is null, userName:" + perm.getUserName());
continue;
}
if (!enabledOnly || user.getEnabled()) {
users.add(user);
}
}
return users;
}
@Override
public WeblogTemplate getTemplate(String id) throws WebloggerException {
// Don't hit database for templates stored on disk
if (id != null && id.endsWith(".vm")) {
return null;
}
return (WeblogTemplate)this.strategy.load(WeblogTemplate.class,id);
}
/**
* Use JPA directly because Weblogger's Query API does too much allocation.
*/
@Override
public WeblogTemplate getTemplateByLink(Weblog weblog, String templateLink)
throws WebloggerException {
if (weblog == null) {
throw new WebloggerException("userName is null");
}
if (templateLink == null) {
throw new WebloggerException("templateLink is null");
}
TypedQuery<WeblogTemplate> query = strategy.getNamedQuery("WeblogTemplate.getByWeblog&Link",
WeblogTemplate.class);
query.setParameter(1, weblog);
query.setParameter(2, templateLink);
try {
return query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* @see org.apache.roller.weblogger.business.WeblogManager#getTemplateByAction(Weblog, ComponentType)
*/
@Override
public WeblogTemplate getTemplateByAction(Weblog weblog, ComponentType action)
throws WebloggerException {
if (weblog == null) {
throw new WebloggerException("weblog is null");
}
if (action == null) {
throw new WebloggerException("Action name is null");
}
TypedQuery<WeblogTemplate> query = strategy.getNamedQuery("WeblogTemplate.getByAction",
WeblogTemplate.class);
query.setParameter(1, weblog);
query.setParameter(2, action);
try {
return query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* @see org.apache.roller.weblogger.business.WeblogManager#getTemplateByName(Weblog, java.lang.String)
*/
@Override
public WeblogTemplate getTemplateByName(Weblog weblog, String templateName)
throws WebloggerException {
if (weblog == null) {
throw new WebloggerException("weblog is null");
}
if (templateName == null) {
throw new WebloggerException("Template name is null");
}
TypedQuery<WeblogTemplate> query = strategy.getNamedQuery("WeblogTemplate.getByWeblog&Name",
WeblogTemplate.class);
query.setParameter(1, weblog);
query.setParameter(2, templateName);
try {
return query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
/**
* @see org.apache.roller.weblogger.business.WeblogManager#getTemplates(Weblog)
*/
@Override
public List<WeblogTemplate> getTemplates(Weblog weblog) throws WebloggerException {
if (weblog == null) {
throw new WebloggerException("weblog is null");
}
TypedQuery<WeblogTemplate> q = strategy.getNamedQuery(
"WeblogTemplate.getByWeblogOrderByName", WeblogTemplate.class);
q.setParameter(1, weblog);
return q.getResultList();
}
@Override
public Map<String, Long> getWeblogHandleLetterMap() throws WebloggerException {
String lc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Map<String, Long> results = new TreeMap<>();
TypedQuery<Long> query = strategy.getNamedQuery(
"Weblog.getCountByHandleLike", Long.class);
for (int i=0; i<26; i++) {
char currentChar = lc.charAt(i);
query.setParameter(1, currentChar + "%");
List<Long> row = query.getResultList();
Long count = row.get(0);
results.put(String.valueOf(currentChar), count);
}
return results;
}
@Override
public List<Weblog> getWeblogsByLetter(char letter, int offset, int length)
throws WebloggerException {
TypedQuery<Weblog> query = strategy.getNamedQuery(
"Weblog.getByLetterOrderByHandle", Weblog.class);
query.setParameter(1, letter + "%");
if (offset != 0) {
query.setFirstResult(offset);
}
if (length != -1) {
query.setMaxResults(length);
}
return query.getResultList();
}
@Override
public List<StatCount> getMostCommentedWeblogs(Date startDate, Date endDate,
int offset, int length)
throws WebloggerException {
Query query;
if (endDate == null) {
endDate = new Date();
}
if (startDate != null) {
Timestamp start = new Timestamp(startDate.getTime());
Timestamp end = new Timestamp(endDate.getTime());
query = strategy.getNamedQuery(
"WeblogEntryComment.getMostCommentedWebsiteByEndDate&StartDate");
query.setParameter(1, end);
query.setParameter(2, start);
} else {
Timestamp end = new Timestamp(endDate.getTime());
query = strategy.getNamedQuery(
"WeblogEntryComment.getMostCommentedWebsiteByEndDate");
query.setParameter(1, end);
}
if (offset != 0) {
query.setFirstResult(offset);
}
if (length != -1) {
query.setMaxResults(length);
}
List<?> queryResults = query.getResultList();
List<StatCount> results = new ArrayList<>();
if (queryResults != null) {
for (Object obj : queryResults) {
Object[] row = (Object[]) obj;
StatCount sc = new StatCount(
(String)row[1], // weblog id
(String)row[2], // weblog handle
(String)row[3], // weblog name
"statCount.weblogCommentCountType", // stat type
((Long)row[0])); // # comments
sc.setWeblogHandle((String)row[2]);
results.add(sc);
}
}
// Original query ordered by desc # comments.
// JPA QL doesn't allow queries to be ordered by aggregates; do it in memory
results.sort(STAT_COUNT_COUNT_REVERSE_COMPARATOR);
return results;
}
/**
* Get count of weblogs, active and inactive
*/
@Override
public long getWeblogCount() throws WebloggerException {
List<Long> results = strategy.getNamedQuery(
"Weblog.getCountAllDistinct", Long.class).getResultList();
return results.get(0);
}
/**
* Returns true if alphanumeric or '_'.
*/
private boolean isAlphanumeric(String str) {
if (str == null) {
return false;
}
for (int i = 0; i < str.length(); i++) {
if (!Character.isLetterOrDigit(str.charAt(i)) && str.charAt(i) != '_') {
return false;
}
}
return true;
}
}