diff --git a/Dockerfile.tomcat b/Dockerfile.tomcat
index 2a1629224f..fc3a0bb4f2 100644
--- a/Dockerfile.tomcat
+++ b/Dockerfile.tomcat
@@ -6,7 +6,7 @@ LABEL name="Entando App" \
maintainer="dev@entando.com" \
vendor="Entando Inc." \
version="${VERSION}" \
- release="7.3.0-fix.2" \
+ release="7.3.0-fix.4" \
summary="Entando Application" \
description="This Entando app engine application provides APIs and composition for Entando applications"
diff --git a/admin-console/pom.xml b/admin-console/pom.xml
index 7100e9d169..8fcb5d453c 100644
--- a/admin-console/pom.xml
+++ b/admin-console/pom.xml
@@ -4,7 +4,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
org.entando.entando
entando-admin-console
diff --git a/cds-plugin/pom.xml b/cds-plugin/pom.xml
index 958673c107..9f726cca18 100644
--- a/cds-plugin/pom.xml
+++ b/cds-plugin/pom.xml
@@ -5,7 +5,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
entando-plugin-jpcds
org.entando.entando.plugins
diff --git a/cms-plugin/pom.xml b/cms-plugin/pom.xml
index 44af87182b..8dab3d708a 100644
--- a/cms-plugin/pom.xml
+++ b/cms-plugin/pom.xml
@@ -5,7 +5,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
org.entando.entando.plugins
entando-plugin-jacms
diff --git a/cms-plugin/src/main/webapp/WEB-INF/plugins/jacms/apsadmin/jsp/common/template/subMenu.jsp b/cms-plugin/src/main/webapp/WEB-INF/plugins/jacms/apsadmin/jsp/common/template/subMenu.jsp
index 424d599f72..687cf8557b 100644
--- a/cms-plugin/src/main/webapp/WEB-INF/plugins/jacms/apsadmin/jsp/common/template/subMenu.jsp
+++ b/cms-plugin/src/main/webapp/WEB-INF/plugins/jacms/apsadmin/jsp/common/template/subMenu.jsp
@@ -46,6 +46,17 @@
+
+
+
+
+
+
+
+
+
+
+
jacmsContentManager">
diff --git a/contentscheduler-plugin/pom.xml b/contentscheduler-plugin/pom.xml
index cc53170d13..c6c65a4615 100644
--- a/contentscheduler-plugin/pom.xml
+++ b/contentscheduler-plugin/pom.xml
@@ -4,7 +4,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
entando-plugin-jpcontentscheduler
org.entando.entando.plugins
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentJobs.java b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentJobs.java
index 6bdf5080bf..de843e7a8a 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentJobs.java
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentJobs.java
@@ -21,33 +21,11 @@
*/
package org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
import com.agiletec.aps.system.services.lang.ILangManager;
-import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.ContentThreadConstants;
-import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentState;
-import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentSuspendMove;
-import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.util.Utils;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.scheduling.quartz.QuartzJobBean;
-
import com.agiletec.aps.system.ApsSystemUtils;
import com.agiletec.aps.system.common.entity.model.AttributeFieldError;
import com.agiletec.aps.system.common.entity.model.AttributeTracer;
import com.agiletec.aps.system.common.entity.model.attribute.AttributeInterface;
-import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.aps.system.services.category.Category;
import com.agiletec.aps.system.services.category.ICategoryManager;
import com.agiletec.aps.system.services.page.IPage;
@@ -58,10 +36,31 @@
import com.agiletec.plugins.jacms.aps.system.services.content.model.Content;
import com.agiletec.plugins.jacms.aps.system.services.contentmodel.ContentModel;
import com.agiletec.plugins.jacms.aps.system.services.contentmodel.IContentModelManager;
+import org.entando.entando.ent.exception.EntException;
+import org.entando.entando.ent.util.EntLogging.EntLogger;
+import org.entando.entando.ent.util.EntLogging.EntLogFactory;
+import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.ContentThreadConstants;
+import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentState;
+import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentSuspendMove;
+import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.util.Utils;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
public class ContentJobs extends QuartzJobBean implements ApplicationContextAware {
- private static final Logger _logger = LoggerFactory.getLogger(ContentJobs.class);
+ private static final EntLogger _logger = EntLogFactory.getSanitizedLogger(ContentJobs.class);
private static final String APPLICATION_CONTEXT_KEY = "applicationContext";
@@ -70,12 +69,10 @@ public class ContentJobs extends QuartzJobBean implements ApplicationContextAwar
private ICategoryManager _categoryManager;
private IPageManager _pageManager;
private IContentModelManager _contentModelManager;
+ private ILangManager _langManager;
private ApplicationContext _ctx;
- @Autowired
- private ILangManager langManager;
-
@Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
this._ctx = ac;
@@ -97,6 +94,7 @@ private void initBeans(ApplicationContext appCtx) {
this.setContentModelManager((IContentModelManager) appCtx.getBean("jacmsContentModelManager"));
this.setCategoryManager((ICategoryManager) appCtx.getBean("CategoryManager"));
this.setPageManager((IPageManager) appCtx.getBean("PageManager"));
+ this.setLangManager((ILangManager) appCtx.getBean("LangManager"));
}
@Override
@@ -116,7 +114,7 @@ public void executeJob(ApplicationContext appCtx) throws JobExecutionException {
if (this.getContentSchedulerManager().getConfig()
.isActive()/* && isCurrentSiteAllowed() */) {
Date startJobDate = new Date();
- _logger.info(ContentThreadConstants.START_TIME_LOG + Utils.printTimeStamp(startJobDate));
+ _logger.info(ContentThreadConstants.START_TIME_LOG + "{}", Utils.printTimeStamp(startJobDate));
List removedContents = new ArrayList();
List publishedContents = new ArrayList();
List moveContents = new ArrayList();
@@ -130,11 +128,11 @@ public void executeJob(ApplicationContext appCtx) throws JobExecutionException {
Collections.sort(removedContents);
Collections.sort(moveContents);
Date endJobDate = new Date();
- _logger.info(ContentThreadConstants.END_TIME_LOG + Utils.printTimeStamp(endJobDate));
+ _logger.info(ContentThreadConstants.END_TIME_LOG + "{}", Utils.printTimeStamp(endJobDate));
this.getContentSchedulerManager().sendMailWithResults(publishedContents, removedContents,
moveContents, startJobDate, endJobDate);
} catch (Throwable t) {
- throw new ApsSystemException(ContentThreadConstants.ERROR_ON_MAIL, t);
+ throw new EntException(ContentThreadConstants.ERROR_ON_MAIL, t);
}
} catch (Throwable t) {
ApsSystemUtils.logThrowable(t, this, t.getMessage());
@@ -147,7 +145,7 @@ public void executeJob(ApplicationContext appCtx) throws JobExecutionException {
}
}
- private void publishContentsJob(List publishedContents) throws ApsSystemException {
+ private void publishContentsJob(List publishedContents) throws EntException {
try {
// Restituisce gli id dei contenuti che hanno un attributo con nome
// key Data_inizio e valore la data corrente
@@ -164,24 +162,21 @@ private void publishContentsJob(List publishedContents) throws Aps
if (null == contentToPublish) {
publishedContents.add(new ContentState(contentId, "null", "null",
ContentThreadConstants.PUBLISH_ACTION, ContentThreadConstants.NULL_CONTENT));
- _logger.info("Pubblicazione automatica non riuscita: " + contentId + " - "
- + ContentThreadConstants.NULL_CONTENT);
+ _logger.info("Pubblicazione automatica non riuscita: {} - " + ContentThreadConstants.NULL_CONTENT, contentId);
continue;
}
if (contentToPublish.isOnLine()) {
publishedContents.add(new ContentState(contentToPublish.getId(), contentToPublish.getTypeCode(),
contentToPublish.getDescription(), ContentThreadConstants.PUBLISH_ACTION,
ContentThreadConstants.ISALREADYONLINE));
- _logger.info("Pubblicazione automatica non riuscita: " + contentToPublish.getId() + " - "
- + ContentThreadConstants.ISALREADYONLINE);
+ _logger.info("Pubblicazione automatica non riuscita: {} - " + ContentThreadConstants.ISALREADYONLINE, contentToPublish.getId());
continue;
}
if (!Content.STATUS_READY.equals(contentToPublish.getStatus())) {
publishedContents.add(new ContentState(contentToPublish.getId(), contentToPublish.getTypeCode(),
contentToPublish.getDescription(), ContentThreadConstants.PUBLISH_ACTION,
ContentThreadConstants.NOTREADYSTATUS));
- _logger.info("Pubblicazione automatica non riuscita: " + contentToPublish.getId() + " - "
- + ContentThreadConstants.NOTREADYSTATUS);
+ _logger.info("Pubblicazione automatica non riuscita: {} - " + ContentThreadConstants.NOTREADYSTATUS, contentToPublish.getId());
continue;
}
boolean validation = this.scanEntity(contentToPublish);
@@ -189,14 +184,13 @@ private void publishContentsJob(List publishedContents) throws Aps
publishedContents.add(new ContentState(contentToPublish.getId(), contentToPublish.getTypeCode(),
contentToPublish.getDescription(), ContentThreadConstants.PUBLISH_ACTION,
ContentThreadConstants.CONTENTWITHERRORS));
- _logger.info("Pubblicazione automatica non riuscita: " + contentToPublish.getId() + " - "
- + ContentThreadConstants.CONTENTWITHERRORS);
+ _logger.info("Pubblicazione automatica non riuscita: {} - " + ContentThreadConstants.CONTENTWITHERRORS, contentToPublish.getId());
continue;
}
// pubblicazione on line del contenuto e modifica data di
// ultima modifica
this.getContentManager().insertOnLineContent(contentToPublish);
- _logger.info("Pubblicato automaticamente contenuto " + contentToPublish.getId());
+ _logger.info("Pubblicato automaticamente contenuto {}", contentToPublish.getId());
publishedContents.add(new ContentState(contentToPublish.getId(), contentToPublish.getTypeCode(),
contentToPublish.getDescription(), ContentThreadConstants.PUBLISH_ACTION,
ContentThreadConstants.ACTION_SUCCESS));
@@ -207,7 +201,7 @@ private void publishContentsJob(List publishedContents) throws Aps
}
}
} catch (Throwable t) {
- throw new ApsSystemException(ContentThreadConstants.ERROR_ON_PUBLISH, t);
+ throw new EntException(ContentThreadConstants.ERROR_ON_PUBLISH, t);
}
}
@@ -217,7 +211,7 @@ public boolean scanEntity(Content currentContent) {
for (int i = 0; i < attributes.size(); i++) {
AttributeInterface entityAttribute = attributes.get(i);
if (entityAttribute.isActive()) {
- List errors = entityAttribute.validate(new AttributeTracer(), langManager);
+ List errors = entityAttribute.validate(new AttributeTracer(), this.getLangManager());
if (null != errors && errors.size() > 0) {
return false;
}
@@ -232,7 +226,7 @@ public boolean scanEntity(Content currentContent) {
@SuppressWarnings("unchecked")
private void suspendOrMoveContentsJob(List removedContents, List moveContents,
- ApplicationContext appCtx) throws ApsSystemException {
+ ApplicationContext appCtx) throws EntException {
try {
// Restituisce gli id dei contenuti che hanno un attributo con nome
// key Data_fine e valore la data corrente
@@ -767,4 +761,7 @@ public void setContentModelManager(IContentModelManager contentModelManager) {
this._contentModelManager = contentModelManager;
}
+ public ILangManager getLangManager() { return _langManager; }
+
+ public void setLangManager(ILangManager langManager) { this._langManager = langManager; }
}
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentSchedulerManager.java b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentSchedulerManager.java
index 59bf16505e..78146a1c43 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentSchedulerManager.java
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/ContentSchedulerManager.java
@@ -26,7 +26,6 @@
import com.agiletec.aps.system.common.AbstractService;
import com.agiletec.aps.system.common.entity.model.EntitySearchFilter;
import com.agiletec.aps.system.common.entity.model.attribute.ITextAttribute;
-import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.aps.system.services.authorization.IApsAuthority;
import com.agiletec.aps.system.services.authorization.IAuthorizationManager;
import com.agiletec.aps.system.services.baseconfig.ConfigInterface;
@@ -213,7 +212,7 @@ public List getContentAttrDataFine() throws EntException {
*/
@Override
public void sendMailWithResults(List publishedContents, List suspendedContents, List movedContents, Date startJobDate,
- Date endJobDate) throws EntException, ApsSystemException {
+ Date endJobDate) throws EntException {
// TODO send to groups
// sendToGroups(publishedContents, suspendedContents);
sendToUsers(publishedContents, suspendedContents, movedContents, startJobDate, endJobDate);
@@ -229,11 +228,10 @@ public void sendMailWithResults(List publishedContents, List publishedContents, List suspendedContents, List moveContents, Date startJobDate, Date endJobDate)
- throws EntException, ApsSystemException {
+ throws EntException {
Map> mapUsers = this.getConfig().getUsersContentType();
Set keys = mapUsers.keySet();
- for (Iterator i = keys.iterator(); i.hasNext();) {
- String key = i.next();
+ for (String key : keys) {
List typesList = mapUsers.get(key);
List contentPList = contentOfTypes(publishedContents, typesList);
List contentSList = contentOfTypes(suspendedContents, typesList);
@@ -241,7 +239,7 @@ private void sendToUsers(List publishedContents, List 0) || (contentSList != null && contentSList.size() > 0) || (contentMList != null && contentMList.size() > 0)) {
UserDetails user = this.getUserManager().getUser(key);
if (user == null) {
- ApsSystemUtils.getLogger().error(ContentThreadConstants.USER_IS_NULL + key);
+ _logger.error(ContentThreadConstants.USER_IS_NULL + "{}", key);
continue;
} else {
UserProfile profile = (UserProfile) user.getProfile();
@@ -251,28 +249,24 @@ private void sendToUsers(List publishedContents, List 0) {
email[0] = mailAttribute.getText();
String simpleText = Utils.prepareMailText(contentPList, contentSList, contentMList, this.getConfig(), startJobDate, endJobDate);
+ boolean issent = false;
if (this.getConfig().isAlsoHtml()) {
String applBaseUrl = this.getConfigManager().getParam(SystemConstants.PAR_APPL_BASE_URL);
String htmlText = Utils.prepareMailHtml(contentPList, contentSList, contentMList, this.getConfig(), startJobDate, endJobDate, applBaseUrl);
- boolean issent = this.getMailManager().sendMixedMail(simpleText, htmlText, config.getSubject(), null, email, null, null, config.getSenderCode());
+ issent = this.getMailManager().sendMixedMail(simpleText, htmlText, config.getSubject(), null, email, null, null, config.getSenderCode());
// System.out.println("***MAIL html");
- if (issent) {
- ApsSystemUtils.getLogger().info(ContentThreadConstants.MAIL_SENT + key);
- } else {
- ApsSystemUtils.getLogger().error(ContentThreadConstants.SEND_ERROR + key);
- }
} else {
// System.out.println("***MAIL simple");
- boolean issent = this.getMailManager().sendMail(simpleText, config.getSubject(), email, null, null, config.getSenderCode());
- if (issent) {
- ApsSystemUtils.getLogger().info(ContentThreadConstants.MAIL_SENT + key);
- } else {
- ApsSystemUtils.getLogger().error(ContentThreadConstants.SEND_ERROR + key);
- }
+ issent = this.getMailManager().sendMail(simpleText, config.getSubject(), email, null, null, config.getSenderCode());
+ }
+ if (issent) {
+ _logger.info(ContentThreadConstants.MAIL_SENT + "{}", key);
+ } else {
+ _logger.error(ContentThreadConstants.SEND_ERROR + "{}", key);
}
}
} else {
- ApsSystemUtils.getLogger().error(ContentThreadConstants.PROFILE_IS_NULL + key);
+ _logger.error(ContentThreadConstants.PROFILE_IS_NULL + "{}", key);
}
}
}
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/IContentSchedulerManager.java b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/IContentSchedulerManager.java
index 4a8dadf043..dd19f1db28 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/IContentSchedulerManager.java
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/IContentSchedulerManager.java
@@ -21,7 +21,6 @@
*/
package org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content;
-import com.agiletec.aps.system.exception.ApsSystemException;
import java.util.Date;
import java.util.List;
@@ -65,7 +64,7 @@ public interface IContentSchedulerManager {
public void updateConfig(ContentThreadConfig config) throws EntException;
public void sendMailWithResults(List publishedContents, List suspendedContents, List moveContents, Date startJobDate, Date endJobDate)
- throws EntException, ApsSystemException;
+ throws EntException;
/**
* Return the desired system parameter
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/parse/ContentThreadConfigDOM.java b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/parse/ContentThreadConfigDOM.java
index 7871ab9109..6beb092f74 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/parse/ContentThreadConfigDOM.java
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/parse/ContentThreadConfigDOM.java
@@ -32,6 +32,7 @@
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
+import org.entando.entando.ent.exception.EntException;
import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentThreadConfig;
import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentTypeElem;
import org.jdom.CDATA;
@@ -42,7 +43,6 @@
import org.jdom.output.XMLOutputter;
import com.agiletec.aps.system.ApsSystemUtils;
-import com.agiletec.aps.system.exception.ApsSystemException;
/**
* Classe DOM delegata alle operazioni di lettura/scrittura della configurazione
@@ -56,10 +56,10 @@ public class ContentThreadConfigDOM {
* @param xml
* The xml containing the configuration.
* @return The contentthread configuration.
- * @throws ApsSystemException
+ * @throws EntException
* In case of parsing errors.
*/
- public ContentThreadConfig extractConfig(String xml) throws ApsSystemException {
+ public ContentThreadConfig extractConfig(String xml) throws EntException {
ContentThreadConfig config = new ContentThreadConfig();
config.setGroupsContentType(new HashMap<>());
config.setUsersContentType(new HashMap<>());
@@ -80,10 +80,10 @@ public ContentThreadConfig extractConfig(String xml) throws ApsSystemException {
* @param config
* The contentThread configuration.
* @return The xml containing the configuration.
- * @throws ApsSystemException
+ * @throws EntException
* In case of errors.
*/
- public String createConfigXml(ContentThreadConfig config) throws ApsSystemException {
+ public String createConfigXml(ContentThreadConfig config) throws EntException {
Element root = this.createConfigElement(config);
Document doc = new Document(root);
String xml = new XMLOutputter().outputString(doc);
@@ -440,10 +440,10 @@ private Element createMailElement(ContentThreadConfig config) {
* @param xmlText
* The text containing an Xml.
* @return The Xml element from a given text.
- * @throws ApsSystemException
+ * @throws EntException
* In case of parsing exceptions.
*/
- private Element getRootElement(String xmlText) throws ApsSystemException {
+ private Element getRootElement(String xmlText) throws EntException {
SAXBuilder builder = new SAXBuilder();
builder.setValidation(false);
StringReader reader = new StringReader(xmlText);
@@ -453,10 +453,10 @@ private Element getRootElement(String xmlText) throws ApsSystemException {
root = doc.getRootElement();
} catch (JDOMException t) {
ApsSystemUtils.getLogger().error("Error parsing xml: " + t.getMessage());
- throw new ApsSystemException("Error parsing xml", t);
+ throw new EntException("Error parsing xml", t);
} catch (IOException t) {
ApsSystemUtils.getLogger().error("Error parsing xml: " + t.getMessage());
- throw new ApsSystemException("Error parsing xml", t);
+ throw new EntException("Error parsing xml", t);
}
return root;
}
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/util/Utils.java b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/util/Utils.java
index 62c2f2ab30..3e7a075929 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/util/Utils.java
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/aps/system/services/content/util/Utils.java
@@ -29,13 +29,13 @@
import java.util.List;
import java.util.Map;
+import org.entando.entando.ent.exception.EntException;
import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.ContentThreadConstants;
import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentState;
import org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.model.ContentThreadConfig;
import org.springframework.context.ApplicationContext;
import com.agiletec.aps.system.ApsSystemUtils;
-import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.plugins.jacms.aps.system.services.content.ContentUtilizer;
import com.agiletec.plugins.jacms.aps.system.services.content.model.Content;
@@ -59,7 +59,7 @@ public static String printTimeStamp(Date date) {
@SuppressWarnings("unchecked")
public static Map getReferencingObjects(Content content, ApplicationContext appCtx)
- throws ApsSystemException {
+ throws EntException {
Map references = new HashMap();
try {
// String[] defNames =
@@ -82,7 +82,7 @@ public static Map getReferencingObjects(Content content, Applicati
}
}
} catch (Throwable t) {
- throw new ApsSystemException("Errore in hasReferencingObject", t);
+ throw new EntException("Errore in hasReferencingObject", t);
}
return references;
}
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigContentTypesAction.java b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigContentTypesAction.java
index 38bfde3569..48b4ac5335 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigContentTypesAction.java
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigContentTypesAction.java
@@ -25,7 +25,6 @@
import java.util.List;
import com.agiletec.aps.system.common.tree.ITreeNode;
-import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.apsadmin.system.ITreeNodeBaseActionHelper;
import com.agiletec.apsadmin.system.TreeNodeWrapper;
import org.apache.commons.lang3.StringUtils;
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigUsersAction.java b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigUsersAction.java
index 5be35e80e1..0aba788b04 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigUsersAction.java
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/ContentThreadConfigUsersAction.java
@@ -33,7 +33,6 @@
import org.slf4j.LoggerFactory;
import com.agiletec.aps.system.common.entity.model.SmallEntityType;
-import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.aps.system.services.baseconfig.ConfigInterface;
import com.agiletec.aps.system.services.user.IUserManager;
import com.agiletec.apsadmin.system.BaseAction;
@@ -59,8 +58,10 @@ public class ContentThreadConfigUsersAction extends BaseAction {
*/
public String viewUsers() {
try {
- this.setConfigItemOnSession();
-
+ // Only initialize session if not already set, to preserve any pending changes
+ if (this.getRequest().getSession().getAttribute(THREAD_CONFIG_SESSION_PARAM_USERS_CONTENT_TYPE) == null) {
+ this.setConfigItemOnSession();
+ }
} catch (Throwable t) {
_logger.error("Error in viewUsers", t);
return FAILURE;
@@ -216,7 +217,7 @@ private boolean validateAdd() throws EntException {
return true;
}
- private boolean validateRemoveContentType() throws ApsSystemException {
+ private boolean validateRemoveContentType() throws EntException {
if (StringUtils.isBlank(this.getUsername())) {
this.addFieldError("username", this.getText("requiredstringByArg", this.getText("username")));
return false;
@@ -229,7 +230,7 @@ private boolean validateRemoveContentType() throws ApsSystemException {
return true;
}
- private boolean validateRemoveUser() throws ApsSystemException {
+ private boolean validateRemoveUser() throws EntException {
if (StringUtils.isBlank(this.getUsername())) {
this.addFieldError("username", this.getText("requiredstringByArg", this.getText("username")));
return false;
@@ -255,8 +256,13 @@ public String saveUsersItem() {
private Map> setConfigItemOnSession() {
Map> usersContentType = this.getContentSchedulerManager().getConfig().getUsersContentType();
- this.getRequest().getSession().setAttribute(THREAD_CONFIG_SESSION_PARAM_USERS_CONTENT_TYPE, usersContentType);
- return usersContentType;
+ // Create mutable copies to allow add/remove operations
+ Map> mutableCopy = new java.util.HashMap<>();
+ if (usersContentType != null) {
+ usersContentType.forEach((key, value) -> mutableCopy.put(key, new ArrayList<>(value)));
+ }
+ this.getRequest().getSession().setAttribute(THREAD_CONFIG_SESSION_PARAM_USERS_CONTENT_TYPE, mutableCopy);
+ return mutableCopy;
}
private void setConfigItemOnSession(Map> config) {
@@ -264,7 +270,11 @@ private void setConfigItemOnSession(Map> config) {
}
public Map> getUsersContentType() {
- return (Map>) this.getRequest().getSession().getAttribute(THREAD_CONFIG_SESSION_PARAM_USERS_CONTENT_TYPE);
+ Map> config = (Map>) this.getRequest().getSession().getAttribute(THREAD_CONFIG_SESSION_PARAM_USERS_CONTENT_TYPE);
+ if (config == null) {
+ config = this.setConfigItemOnSession();
+ }
+ return config;
}
public List getContentTypes() {
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_en.properties b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_en.properties
index 3c916a2c12..e8af10556d 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_en.properties
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_en.properties
@@ -53,7 +53,7 @@ requiredstringByArg=the file {0} is required
legend.User=Users
jpcontentscheduler.label.username=Username
jpcontentscheduler.label.contentTypes=Content Types
-
+jpcontentscheduler.label.contentTypes.all=All Content Types
jpcontentscheduler.label.contentTypes.contentType=Content type
jpcontentscheduler.label.contentTypes.startDate=Start date
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_it.properties b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_it.properties
index e19f15397f..8924375e75 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_it.properties
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/config/package_it.properties
@@ -55,7 +55,7 @@ requiredstringByArg=Il file {0} é richiesto
legend.User=Utenti
jpcontentscheduler.label.username=Username
jpcontentscheduler.label.contentTypes=Tipi di contenuto
-
+jpcontentscheduler.label.contentTypes.all=Tutti i Tipi di Contenuto
jpcontentscheduler.label.contentTypes.contentType=Tipo di contenuto
jpcontentscheduler.label.contentTypes.startDate=Data pubblicazione
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_en.properties b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_en.properties
index c4f7adc063..564049d0e2 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_en.properties
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_en.properties
@@ -1,5 +1,5 @@
jpcontentscheduler.code=jpcontentscheduler
-jpcontentscheduler.name=Content Scheduler
+jpcontentscheduler.name=Scheduler
jpcontentscheduler.title.management=Content Scheduler
diff --git a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_it.properties b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_it.properties
index f53f9dc28f..54b04b86c4 100644
--- a/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_it.properties
+++ b/contentscheduler-plugin/src/main/java/org/entando/entando/plugins/jpcontentscheduler/apsadmin/global-messages_it.properties
@@ -1,5 +1,5 @@
jpcontentscheduler.code=jpcontentscheduler
-jpcontentscheduler.name=Content Scheduler
+jpcontentscheduler.name=Scheduler
jpcontentscheduler.title.management=Content Scheduler Management
diff --git a/contentscheduler-plugin/src/main/resources/spring/plugins/jpcontentscheduler/aps/contentThreadConfig.xml b/contentscheduler-plugin/src/main/resources/spring/plugins/jpcontentscheduler/aps/contentThreadConfig.xml
index 606a30be5b..a32ddc94bb 100644
--- a/contentscheduler-plugin/src/main/resources/spring/plugins/jpcontentscheduler/aps/contentThreadConfig.xml
+++ b/contentscheduler-plugin/src/main/resources/spring/plugins/jpcontentscheduler/aps/contentThreadConfig.xml
@@ -29,6 +29,7 @@
+
diff --git a/engine/pom.xml b/engine/pom.xml
index f8fffd4179..e132740725 100644
--- a/engine/pom.xml
+++ b/engine/pom.xml
@@ -4,7 +4,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
org.entando.entando
entando-engine
diff --git a/engine/src/main/resources/base.xml b/engine/src/main/resources/base.xml
index 4ba04cb7b8..6c73b4a444 100644
--- a/engine/src/main/resources/base.xml
+++ b/engine/src/main/resources/base.xml
@@ -87,4 +87,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/keycloak-plugin/pom.xml b/keycloak-plugin/pom.xml
index 53a50910c8..6372edfdd1 100644
--- a/keycloak-plugin/pom.xml
+++ b/keycloak-plugin/pom.xml
@@ -5,7 +5,7 @@
app-engine
org.entando
- 7.3.0-fix.2
+ 7.3.0-fix.4
org.entando.entando
entando-keycloak-auth
diff --git a/mail-plugin/pom.xml b/mail-plugin/pom.xml
index ffe5ff71d3..602995a48b 100644
--- a/mail-plugin/pom.xml
+++ b/mail-plugin/pom.xml
@@ -4,7 +4,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
entando-plugin-jpmail
org.entando.entando.plugins
diff --git a/pom.xml b/pom.xml
index 00264590f3..f7fdeb2dd9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
pom
diff --git a/portal-ui/pom.xml b/portal-ui/pom.xml
index 0ccc9e937c..1e61498ffb 100644
--- a/portal-ui/pom.xml
+++ b/portal-ui/pom.xml
@@ -4,7 +4,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
org.entando.entando
entando-portal-ui
diff --git a/redis-plugin/pom.xml b/redis-plugin/pom.xml
index 361b7afd40..64a9a66f76 100644
--- a/redis-plugin/pom.xml
+++ b/redis-plugin/pom.xml
@@ -5,7 +5,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
entando-plugin-jpredis
org.entando.entando.plugins
diff --git a/seo-plugin/pom.xml b/seo-plugin/pom.xml
index 5d5bf6029d..ca2fe65474 100644
--- a/seo-plugin/pom.xml
+++ b/seo-plugin/pom.xml
@@ -4,7 +4,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
entando-plugin-jpseo
org.entando.entando.plugins
diff --git a/solr-plugin/pom.xml b/solr-plugin/pom.xml
index 601ad5cf27..401cca4fd3 100644
--- a/solr-plugin/pom.xml
+++ b/solr-plugin/pom.xml
@@ -5,7 +5,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
entando-plugin-jpsolr
org.entando.entando.plugins
diff --git a/versioning-plugin/pom.xml b/versioning-plugin/pom.xml
index d8f1ba15d6..68188282c3 100644
--- a/versioning-plugin/pom.xml
+++ b/versioning-plugin/pom.xml
@@ -4,7 +4,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
entando-plugin-jpversioning
org.entando.entando.plugins
diff --git a/versioning-plugin/src/main/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/VersioningManager.java b/versioning-plugin/src/main/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/VersioningManager.java
index 232e5783d2..2c4723ccd3 100644
--- a/versioning-plugin/src/main/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/VersioningManager.java
+++ b/versioning-plugin/src/main/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/VersioningManager.java
@@ -190,18 +190,54 @@ public void saveContentVersion(String contentId) throws EntException {
this.deleteWorkVersions(versionRecord.getContentId(), onlineVersionsToDelete);
}
if (null == this.getVersioningDAO().getVersion(contentId, versionRecord.getVersion())) {
- this.getVersioningDAO().addContentVersion(versionRecord);
+ try {
+ this.getVersioningDAO().addContentVersion(versionRecord);
+ } catch (RuntimeException e) {
+ if (isDuplicateKeyException(e)) {
+ _logger.warn("ContentId '{}' - version '{}' already exists (concurrent insert)", contentId, versionRecord.getVersion());
+ } else {
+ throw e;
+ }
+ }
} else {
- logger.warn("ContentId '{}' - version '{}' already exists", contentId, versionRecord.getVersion());
+ _logger.warn("ContentId '{}' - version '{}' already exists", contentId, versionRecord.getVersion());
}
}
}
+ } catch (EntException e) {
+ throw e;
} catch (Exception e) {
_logger.error("error in Error saving version for content {}", contentId, e);
throw new EntException("Error saving version for content" + contentId);
}
}
+ private boolean isDuplicateKeyException(Throwable e) {
+ while (e != null) {
+ if (e instanceof java.sql.SQLException) {
+ java.sql.SQLException sqlEx = (java.sql.SQLException) e;
+ String sqlState = sqlEx.getSQLState();
+ if (sqlState != null) {
+ // 23505: unique_violation (PostgreSQL, Derby)
+ // 23000: integrity constraint violation (MySQL, Oracle - need to check error code)
+ if ("23505".equals(sqlState)) {
+ return true;
+ }
+ if ("23000".equals(sqlState)) {
+ int errorCode = sqlEx.getErrorCode();
+ // MySQL: 1062 (ER_DUP_ENTRY), 1586 (ER_DUP_ENTRY_WITH_KEY_NAME)
+ // Oracle: 1 (ORA-00001: unique constraint violated)
+ if (errorCode == 1062 || errorCode == 1586 || errorCode == 1) {
+ return true;
+ }
+ }
+ }
+ }
+ e = e.getCause();
+ }
+ return false;
+ }
+
@Override
public void deleteWorkVersions(String contentId, int onlineVersion) throws EntException {
try {
diff --git a/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/DuplicateKeyExceptionTest.java b/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/DuplicateKeyExceptionTest.java
new file mode 100644
index 0000000000..fce8684762
--- /dev/null
+++ b/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/DuplicateKeyExceptionTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.agiletec.plugins.jpversioning.aps.system.services.versioning;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for duplicate key exception detection across different JDBC databases.
+ */
+@ExtendWith(MockitoExtension.class)
+class DuplicateKeyExceptionTest {
+
+ private VersioningManager versioningManager;
+ private Method isDuplicateKeyExceptionMethod;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ versioningManager = new VersioningManager();
+ // Access the private method via reflection
+ isDuplicateKeyExceptionMethod = VersioningManager.class.getDeclaredMethod("isDuplicateKeyException", Throwable.class);
+ isDuplicateKeyExceptionMethod.setAccessible(true);
+ }
+
+ private boolean invokeIsDuplicateKeyException(Throwable e) throws Exception {
+ return (Boolean) isDuplicateKeyExceptionMethod.invoke(versioningManager, e);
+ }
+
+ // ==================== PostgreSQL Tests ====================
+
+ @Test
+ void testPostgreSQLDuplicateKey() throws Exception {
+ // PostgreSQL uses SQLState 23505 for unique_violation
+ SQLException sqlEx = new SQLException("duplicate key value violates unique constraint", "23505");
+ assertTrue(invokeIsDuplicateKeyException(sqlEx), "PostgreSQL duplicate key should be detected");
+ }
+
+ @Test
+ void testPostgreSQLDuplicateKeyWrappedInRuntimeException() throws Exception {
+ SQLException sqlEx = new SQLException("duplicate key value violates unique constraint", "23505");
+ RuntimeException wrapped = new RuntimeException("Error adding version record", sqlEx);
+ assertTrue(invokeIsDuplicateKeyException(wrapped), "Wrapped PostgreSQL duplicate key should be detected");
+ }
+
+ // ==================== Derby Tests ====================
+
+ @Test
+ void testDerbyDuplicateKey() throws Exception {
+ // Derby also uses SQLState 23505 for duplicate key
+ SQLException sqlEx = new SQLException("The statement was aborted because it would have caused a duplicate key value", "23505");
+ assertTrue(invokeIsDuplicateKeyException(sqlEx), "Derby duplicate key should be detected");
+ }
+
+ @Test
+ void testDerbyDuplicateKeyWrappedInRuntimeException() throws Exception {
+ SQLException sqlEx = new SQLException("The statement was aborted because it would have caused a duplicate key value", "23505");
+ RuntimeException wrapped = new RuntimeException("Error adding version record", sqlEx);
+ assertTrue(invokeIsDuplicateKeyException(wrapped), "Wrapped Derby duplicate key should be detected");
+ }
+
+ // ==================== MySQL Tests ====================
+
+ @Test
+ void testMySQLDuplicateKey_ErrorCode1062() throws Exception {
+ // MySQL uses SQLState 23000 with error code 1062 (ER_DUP_ENTRY)
+ SQLException sqlEx = new SQLException("Duplicate entry 'value' for key 'PRIMARY'", "23000", 1062);
+ assertTrue(invokeIsDuplicateKeyException(sqlEx), "MySQL duplicate key (1062) should be detected");
+ }
+
+ @Test
+ void testMySQLDuplicateKey_ErrorCode1586() throws Exception {
+ // MySQL uses SQLState 23000 with error code 1586 (ER_DUP_ENTRY_WITH_KEY_NAME)
+ SQLException sqlEx = new SQLException("Duplicate entry 'value' for key 'key_name'", "23000", 1586);
+ assertTrue(invokeIsDuplicateKeyException(sqlEx), "MySQL duplicate key (1586) should be detected");
+ }
+
+ @Test
+ void testMySQLDuplicateKeyWrappedInRuntimeException() throws Exception {
+ SQLException sqlEx = new SQLException("Duplicate entry 'value' for key 'PRIMARY'", "23000", 1062);
+ RuntimeException wrapped = new RuntimeException("Error adding version record", sqlEx);
+ assertTrue(invokeIsDuplicateKeyException(wrapped), "Wrapped MySQL duplicate key should be detected");
+ }
+
+ // ==================== Oracle Tests ====================
+
+ @Test
+ void testOracleDuplicateKey() throws Exception {
+ // Oracle uses SQLState 23000 with error code 1 (ORA-00001: unique constraint violated)
+ SQLException sqlEx = new SQLException("ORA-00001: unique constraint (SCHEMA.CONSTRAINT_NAME) violated", "23000", 1);
+ assertTrue(invokeIsDuplicateKeyException(sqlEx), "Oracle duplicate key should be detected");
+ }
+
+ @Test
+ void testOracleDuplicateKeyWrappedInRuntimeException() throws Exception {
+ SQLException sqlEx = new SQLException("ORA-00001: unique constraint (SCHEMA.CONSTRAINT_NAME) violated", "23000", 1);
+ RuntimeException wrapped = new RuntimeException("Error adding version record", sqlEx);
+ assertTrue(invokeIsDuplicateKeyException(wrapped), "Wrapped Oracle duplicate key should be detected");
+ }
+
+ // ==================== Negative Tests ====================
+
+ @Test
+ void testNonDuplicateKeyException() throws Exception {
+ // A general SQL exception that is not a duplicate key
+ SQLException sqlEx = new SQLException("Connection refused", "08001");
+ assertFalse(invokeIsDuplicateKeyException(sqlEx), "Non-duplicate key exception should not be detected");
+ }
+
+ @Test
+ void testForeignKeyViolation_MySQL() throws Exception {
+ // MySQL foreign key violation: SQLState 23000 with error code 1452
+ SQLException sqlEx = new SQLException("Cannot add or update a child row: a foreign key constraint fails", "23000", 1452);
+ assertFalse(invokeIsDuplicateKeyException(sqlEx), "MySQL foreign key violation should not be detected as duplicate key");
+ }
+
+ @Test
+ void testForeignKeyViolation_PostgreSQL() throws Exception {
+ // PostgreSQL foreign key violation: SQLState 23503
+ SQLException sqlEx = new SQLException("insert or update on table violates foreign key constraint", "23503");
+ assertFalse(invokeIsDuplicateKeyException(sqlEx), "PostgreSQL foreign key violation should not be detected as duplicate key");
+ }
+
+ @Test
+ void testNullException() throws Exception {
+ assertFalse(invokeIsDuplicateKeyException(null), "Null exception should return false");
+ }
+
+ @Test
+ void testNullSQLState() throws Exception {
+ SQLException sqlEx = new SQLException("Some error", (String) null);
+ assertFalse(invokeIsDuplicateKeyException(sqlEx), "Null SQL state should return false");
+ }
+
+ @Test
+ void testNonSQLException() throws Exception {
+ RuntimeException ex = new RuntimeException("Some error");
+ assertFalse(invokeIsDuplicateKeyException(ex), "Non-SQLException without cause should return false");
+ }
+
+ @Test
+ void testDeeplyNestedSQLException() throws Exception {
+ SQLException sqlEx = new SQLException("duplicate key", "23505");
+ RuntimeException level1 = new RuntimeException("Level 1", sqlEx);
+ RuntimeException level2 = new RuntimeException("Level 2", level1);
+ RuntimeException level3 = new RuntimeException("Level 3", level2);
+ assertTrue(invokeIsDuplicateKeyException(level3), "Deeply nested SQLException should be detected");
+ }
+
+ @Test
+ void testIntegrityConstraint23000WithoutMatchingErrorCode() throws Exception {
+ // SQLState 23000 but with an error code that doesn't match duplicate key
+ SQLException sqlEx = new SQLException("Some other constraint violation", "23000", 9999);
+ assertFalse(invokeIsDuplicateKeyException(sqlEx), "SQLState 23000 with non-matching error code should return false");
+ }
+}
\ No newline at end of file
diff --git a/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/TestVersioningManager.java b/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/TestVersioningManager.java
index e443263c05..5f5ecb5026 100644
--- a/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/TestVersioningManager.java
+++ b/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/TestVersioningManager.java
@@ -21,12 +21,6 @@
*/
package com.agiletec.plugins.jpversioning.aps.system.services.versioning;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
import com.agiletec.aps.BaseTestCase;
import com.agiletec.aps.system.SystemConstants;
import com.agiletec.aps.system.services.baseconfig.ConfigInterface;
@@ -47,6 +41,9 @@
import java.util.Map;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
/**
* @author G.Cocco
@@ -58,6 +55,22 @@ public class TestVersioningManager extends BaseTestCase {
private IVersioningManager versioningManager;
private JpversioningTestHelper helper;
+ @BeforeEach
+ public void init() throws Exception {
+ this.versioningManager = (IVersioningManager) this.getService(JpversioningSystemConstants.VERSIONING_MANAGER);
+ this.configManager = (ConfigInterface) this.getService(SystemConstants.BASE_CONFIG_MANAGER);
+ this.contentManager = (IContentManager) this.getService(JacmsSystemConstants.CONTENT_MANAGER);
+ DataSource dataSource = (DataSource) this.getApplicationContext().getBean("portDataSource");
+ this.helper = new JpversioningTestHelper(dataSource, this.getApplicationContext());
+ this.helper.initContentVersions();
+ }
+
+ @AfterEach
+ public void dispose() throws Exception {
+ this.helper.cleanContentVersions();
+ }
+
+ @Test
void testGetVersions() throws Throwable {
List versions = this.versioningManager.getVersions("CNG12");
assertNull(versions);
@@ -66,6 +79,7 @@ void testGetVersions() throws Throwable {
this.checkVersionIds(new long[]{1, 2, 3}, versions);
}
+ @Test
void testGetLastVersions() throws Throwable {
List versions = this.versioningManager.getLastVersions("CNG", null);
assertTrue(versions.isEmpty());
@@ -74,6 +88,7 @@ void testGetLastVersions() throws Throwable {
this.checkVersionIds(new long[]{3}, versions);
}
+ @Test
void testGetVersion() throws Throwable {
ContentVersion contentVersion = this.versioningManager.getVersion(10000);
assertNull(contentVersion);
@@ -92,6 +107,7 @@ void testGetVersion() throws Throwable {
assertEquals("admin", contentVersion.getUsername());
}
+ @Test
void testGetLastVersion() throws Throwable {
ContentVersion contentVersion = this.versioningManager.getLastVersion("CNG12");
assertNull(contentVersion);
@@ -109,6 +125,7 @@ void testGetLastVersion() throws Throwable {
assertEquals("mainEditor", contentVersion.getUsername());
}
+ @Test
void testSaveGetDeleteVersion() throws Throwable {
((VersioningManager) this.versioningManager).saveContentVersion("ART102");
ContentVersion contentVersion = this.versioningManager.getLastVersion("ART102");
@@ -128,14 +145,23 @@ void testSaveGetDeleteVersion() throws Throwable {
assertNull(this.versioningManager.getLastVersion("ART102"));
}
+ @Test
public void deleteWorkVersions() throws Throwable {
- List versions = this.versioningManager.getVersions("ART1");
- this.checkVersionIds(new long[]{1, 2, 3}, versions);
- this.versioningManager.deleteWorkVersions("ART1", 0);
- versions = this.versioningManager.getVersions("ART1");
- this.checkVersionIds(new long[]{1, 3}, versions);
+ try {
+ this.updateConfigItem(JpversioningSystemConstants.CONFIG_PARAM_DELETE_MID_VERSIONS, "true");
+ ((VersioningManager) this.versioningManager).initTenantAware();
+ List versions = this.versioningManager.getVersions("ART1");
+ this.checkVersionIds(new long[]{1, 2, 3}, versions);
+ this.versioningManager.deleteWorkVersions("ART1", 0);
+ versions = this.versioningManager.getVersions("ART1");
+ this.checkVersionIds(new long[]{1, 3}, versions);
+ } finally {
+ this.updateConfigItem(JpversioningSystemConstants.CONFIG_PARAM_DELETE_MID_VERSIONS, "");
+ ((VersioningManager) this.versioningManager).initTenantAware();
+ }
}
+
private void checkVersionIds(long[] expected, List received) {
assertEquals(expected.length, received.size());
for (long current : expected) {
@@ -145,16 +171,19 @@ private void checkVersionIds(long[] expected, List received) {
}
}
+ @Test
void testContentVersionToIgnore_1() throws Exception {
this.testContentVersionToIgnore(false, true);
this.testContentVersionToIgnore(true, true);
}
+ @Test
void testContentVersionToIgnore_2() throws Exception {
this.testContentVersionToIgnore(false, false);
this.testContentVersionToIgnore(true, false);
}
+
protected void testContentVersionToIgnore(boolean includeVersion, boolean isType) throws Exception {
String newContentId = null;
List versions = null;
@@ -214,19 +243,5 @@ private void updateConfigItem(String paramKey, String paramValue) throws Excepti
this.configManager.updateConfigItem(SystemConstants.CONFIG_ITEM_PARAMS, newXmlParams);
}
- @BeforeEach
- private void init() throws Exception {
- this.versioningManager = (IVersioningManager) this.getService(JpversioningSystemConstants.VERSIONING_MANAGER);
- this.configManager = (ConfigInterface) this.getService(SystemConstants.BASE_CONFIG_MANAGER);
- this.contentManager = (IContentManager) this.getService(JacmsSystemConstants.CONTENT_MANAGER);
- DataSource dataSource = (DataSource) this.getApplicationContext().getBean("portDataSource");
- this.helper = new JpversioningTestHelper(dataSource, this.getApplicationContext());
- this.helper.initContentVersions();
- }
-
- @AfterEach
- private void dispose() throws Exception {
- this.helper.cleanContentVersions();
- }
}
diff --git a/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/VersioningManagerUnitTest.java b/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/VersioningManagerUnitTest.java
new file mode 100644
index 0000000000..76c902f375
--- /dev/null
+++ b/versioning-plugin/src/test/java/com/agiletec/plugins/jpversioning/aps/system/services/versioning/VersioningManagerUnitTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.agiletec.plugins.jpversioning.aps.system.services.versioning;
+
+import com.agiletec.aps.system.services.baseconfig.ConfigInterface;
+import com.agiletec.plugins.jacms.aps.system.services.content.IContentManager;
+import com.agiletec.plugins.jacms.aps.system.services.content.model.Content;
+import com.agiletec.plugins.jacms.aps.system.services.content.model.ContentRecordVO;
+import org.entando.entando.ent.exception.EntException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class VersioningManagerUnitTest {
+
+ @Mock
+ private IVersioningDAO versioningDAO;
+
+ @Mock
+ private IContentManager contentManager;
+
+ @Mock
+ private ConfigInterface configManager;
+
+ @Spy
+ @InjectMocks
+ private VersioningManager versioningManager;
+
+ private ContentRecordVO mockContentRecord;
+
+ @BeforeEach
+ void setUp() {
+ mockContentRecord = new ContentRecordVO();
+ mockContentRecord.setId("ART123");
+ mockContentRecord.setTypeCode("ART");
+ mockContentRecord.setDescription("Test Content");
+ mockContentRecord.setStatus(Content.STATUS_DRAFT);
+ mockContentRecord.setXmlWork("test");
+ mockContentRecord.setModify(new Date());
+ mockContentRecord.setVersion("1.0");
+ mockContentRecord.setLastEditor("admin");
+ }
+
+ @Test
+ void testSaveContentVersion_ThrowsExceptionForNonDuplicateKeyError() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate a generic RuntimeException (not duplicate key)
+ RuntimeException genericException = new RuntimeException("Database connection failed");
+ doThrow(genericException).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute and verify - should throw EntException wrapping the original exception
+ EntException thrown = assertThrows(EntException.class,
+ () -> versioningManager.saveContentVersion("ART123"));
+
+ assertEquals("Error saving version for contentART123", thrown.getMessage());
+ }
+
+ @Test
+ void testSaveContentVersion_HandlesUniqueConstraintViolationGracefully() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate a duplicate key exception (PostgreSQL/Derby SQLState 23505)
+ SQLException sqlException = new SQLException("unique constraint violation", "23505");
+ RuntimeException duplicateKeyException = new RuntimeException("Error adding version record", sqlException);
+ doThrow(duplicateKeyException).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute - should NOT throw exception for duplicate key
+ assertDoesNotThrow(() -> versioningManager.saveContentVersion("ART123"));
+
+ // Verify addContentVersion was called
+ verify(versioningDAO).addContentVersion(any(ContentVersion.class));
+ }
+
+ @Test
+ void testSaveContentVersion_HandlesMySQLDuplicateKeyGracefully() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate a MySQL duplicate key exception (SQLState 23000, error code 1062)
+ SQLException sqlException = new SQLException("Duplicate entry for key 'PRIMARY'", "23000", 1062);
+ RuntimeException duplicateKeyException = new RuntimeException("Error adding version record", sqlException);
+ doThrow(duplicateKeyException).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute - should NOT throw exception
+ assertDoesNotThrow(() -> versioningManager.saveContentVersion("ART123"));
+ }
+
+ @Test
+ void testSaveContentVersion_HandlesOracleDuplicateKeyGracefully() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate an Oracle duplicate key exception (SQLState 23000, error code 1)
+ SQLException sqlException = new SQLException("ORA-00001: unique constraint violated", "23000", 1);
+ RuntimeException primaryKeyException = new RuntimeException("Error adding version record", sqlException);
+ doThrow(primaryKeyException).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute - should NOT throw exception
+ assertDoesNotThrow(() -> versioningManager.saveContentVersion("ART123"));
+ }
+
+ @Test
+ void testSaveContentVersion_ThrowsForNullPointerException() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate a NullPointerException (should NOT be treated as duplicate key)
+ NullPointerException npe = new NullPointerException("Some null value");
+ doThrow(npe).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute and verify - should throw EntException
+ assertThrows(EntException.class, () -> versioningManager.saveContentVersion("ART123"));
+ }
+
+ @Test
+ void testSaveContentVersion_ThrowsForIllegalArgumentException() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate an IllegalArgumentException (should NOT be treated as duplicate key)
+ IllegalArgumentException iae = new IllegalArgumentException("Invalid argument");
+ doThrow(iae).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute and verify - should throw EntException
+ assertThrows(EntException.class, () -> versioningManager.saveContentVersion("ART123"));
+ }
+
+ @Test
+ void testSaveContentVersion_SkipsInsertWhenVersionAlreadyExists() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+
+ // Return existing version (not null) - simulates version already exists
+ ContentVersion existingVersion = new ContentVersion();
+ existingVersion.setId(1L);
+ when(versioningDAO.getVersion("ART123", "1.0")).thenReturn(existingVersion);
+
+ // Execute
+ assertDoesNotThrow(() -> versioningManager.saveContentVersion("ART123"));
+
+ // Verify addContentVersion was NEVER called since version already exists
+ verify(versioningDAO, never()).addContentVersion(any(ContentVersion.class));
+ }
+
+ @Test
+ void testSaveContentVersion_HandlesNestedDuplicateKeyException() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate a deeply nested exception where the root cause is a SQLException
+ SQLException sqlException = new SQLException("duplicate key value violates unique constraint", "23505");
+ RuntimeException cause = new RuntimeException("Database error", sqlException);
+ RuntimeException wrapper = new RuntimeException("Insert failed", cause);
+ doThrow(wrapper).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute - should NOT throw exception because nested cause has duplicate key
+ assertDoesNotThrow(() -> versioningManager.saveContentVersion("ART123"));
+ }
+
+ @Test
+ void testSaveContentVersion_ThrowsForNestedNonDuplicateException() throws Exception {
+ // Setup
+ when(contentManager.loadContentVO("ART123")).thenReturn(mockContentRecord);
+ when(versioningDAO.getVersion(anyString(), anyString())).thenReturn(null);
+
+ // Simulate a nested exception without duplicate key indicators
+ RuntimeException cause = new RuntimeException("Connection timeout");
+ RuntimeException wrapper = new RuntimeException("Database error", cause);
+ doThrow(wrapper).when(versioningDAO).addContentVersion(any(ContentVersion.class));
+
+ // Execute and verify - should throw EntException
+ assertThrows(EntException.class, () -> versioningManager.saveContentVersion("ART123"));
+ }
+}
\ No newline at end of file
diff --git a/webapp/pom.xml b/webapp/pom.xml
index 1c53727757..a80615bd2a 100644
--- a/webapp/pom.xml
+++ b/webapp/pom.xml
@@ -5,7 +5,7 @@
org.entando
app-engine
- 7.3.0-fix.2
+ 7.3.0-fix.4
org.entando.entando
webapp