Skip to content

Commit c099d77

Browse files
committed
fix: resolve pages requested with trailing slash - fixing /c/ pattern #25264
1 parent b857dcd commit c099d77

4 files changed

Lines changed: 77 additions & 16 deletions

File tree

dotCMS/src/main/java/com/dotmarketing/business/IdentifierFactoryImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,23 @@ protected Identifier saveIdentifier(final Identifier id) throws DotDataException
529529
query = "INSERT INTO identifier (parent_path,asset_name,host_inode,asset_type,syspublish_date,sysexpire_date,owner,create_date,asset_subtype,base_type,id) values (?,?,?,?,?,?,?,?,?,?,?)";
530530
}
531531

532+
// Guard against stale cached Identifiers (loaded before base_type was denormalized)
533+
// writing null back and temporarily undoing the backfill for this row.
534+
if (id.getBaseType() == null && UtilMethods.isSet(id.getAssetSubType())) {
535+
final Integer resolvedBaseType = Try.of(() ->
536+
APILocator.getContentTypeAPI(APILocator.systemUser())
537+
.find(id.getAssetSubType())
538+
.baseType()
539+
.getType())
540+
.onFailure(e -> Logger.warn(IdentifierFactoryImpl.class,
541+
"Could not resolve base_type for asset_subtype=" + id.getAssetSubType()
542+
+ " on identifier=" + id.getId() + ": " + e.getMessage()))
543+
.getOrNull();
544+
if (resolvedBaseType != null) {
545+
id.setBaseType(resolvedBaseType);
546+
}
547+
}
548+
532549
DotConnect dc = new DotConnect();
533550
dc.setSQL(query);
534551

dotCMS/src/main/java/com/dotmarketing/quartz/job/PopulateIdentifierBaseTypeUtil.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import com.dotcms.business.WrapInTransaction;
44
import com.dotmarketing.common.db.DotConnect;
5+
import com.dotmarketing.db.DbConnectionFactory;
56
import com.dotmarketing.exception.DotDataException;
67
import com.dotmarketing.exception.DotRuntimeException;
78
import com.dotmarketing.util.Config;
89
import com.dotmarketing.util.Logger;
10+
import java.sql.Connection;
911

1012
/**
1113
* Populates the {@code base_type} column on the {@code identifier} table in small, independently
@@ -111,11 +113,13 @@ public int populate() {
111113
*
112114
* @return Number of rows updated in this batch; 0 means all rows are already populated.
113115
*/
114-
@WrapInTransaction
115116
private int processBatch() {
116-
try {
117-
return new DotConnect().executeUpdate(UPDATE_BATCH);
118-
} catch (final DotDataException e) {
117+
try(Connection conn = DbConnectionFactory.getDataSource().getConnection()) {
118+
conn.setAutoCommit(false);
119+
int result= new DotConnect().executeUpdate(UPDATE_BATCH, conn);
120+
conn.commit();
121+
return result;
122+
} catch (final Exception e) {
119123
throw new DotRuntimeException(
120124
"PopulateIdentifierBaseType: error executing batch update", e);
121125
}

dotcms-integration/src/test/java/com/dotmarketing/quartz/job/PopulateIdentifierBaseTypeJobTest.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
import org.junit.Test;
1212
import org.quartz.SchedulerException;
1313

14+
import java.util.List;
15+
import java.util.Map;
16+
import java.util.stream.Collectors;
17+
1418
import static org.junit.Assert.assertFalse;
1519
import static org.junit.Assert.assertNotNull;
1620
import static org.junit.Assert.assertNull;
@@ -103,10 +107,21 @@ public void test_removeJob_removesFromScheduler() throws SchedulerException {
103107
public void test_fireJob_skipsSchedulingWhenNoWorkPending() throws Exception {
104108
removeJob();
105109

106-
// Temporarily back-fill any NULLs so hasPendingRows() returns false
107-
new DotConnect().executeStatement(
108-
"UPDATE identifier SET base_type = 1 " +
109-
"WHERE base_type IS NULL AND asset_subtype IS NOT NULL");
110+
// Capture only the IDs that are currently NULL so we restore exactly those rows
111+
final List<String> nullIds = new DotConnect()
112+
.setSQL("SELECT id FROM identifier WHERE base_type IS NULL AND asset_subtype IS NOT NULL")
113+
.loadObjectResults()
114+
.stream()
115+
.map(row -> (String) row.get("id"))
116+
.collect(Collectors.toList());
117+
118+
if (!nullIds.isEmpty()) {
119+
final String idList = nullIds.stream()
120+
.map(id -> "'" + id + "'")
121+
.collect(Collectors.joining(","));
122+
new DotConnect().executeStatement(
123+
"UPDATE identifier SET base_type = 1 WHERE id IN (" + idList + ")");
124+
}
110125

111126
try {
112127
assertFalse("hasPendingRows() should return false when all rows are populated",
@@ -121,9 +136,14 @@ public void test_fireJob_skipsSchedulingWhenNoWorkPending() throws Exception {
121136
final var jobDetail = Try.of(() -> scheduler.getJobDetail(jobName, groupName)).getOrNull();
122137
assertNull("Job should NOT be scheduled when migration is already complete", jobDetail);
123138
} finally {
124-
// Restore NULL so other tests stay valid
125-
new DotConnect().executeStatement(
126-
"UPDATE identifier SET base_type = NULL WHERE base_type = 1 AND asset_subtype IS NOT NULL");
139+
// Restore only the rows we modified
140+
if (!nullIds.isEmpty()) {
141+
final String idList = nullIds.stream()
142+
.map(id -> "'" + id + "'")
143+
.collect(Collectors.joining(","));
144+
new DotConnect().executeStatement(
145+
"UPDATE identifier SET base_type = NULL WHERE id IN (" + idList + ")");
146+
}
127147
}
128148
}
129149

dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task260407AddBaseTypeColumnToIdentifierTest.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import java.sql.Connection;
1212
import java.sql.ResultSet;
1313
import java.sql.SQLException;
14+
import java.util.List;
15+
import java.util.stream.Collectors;
1416

1517
import static org.junit.Assert.assertFalse;
1618
import static org.junit.Assert.assertTrue;
@@ -73,16 +75,34 @@ public void test_forceRun_returnsFalseWhenMigrationComplete() throws DotDataExce
7375
final var task = new Task260407AddBaseTypeColumnToIdentifier();
7476
task.executeUpgrade();
7577

76-
// Ensure no pending rows remain
77-
new DotConnect().executeStatement(
78-
"UPDATE identifier SET base_type = 1 WHERE base_type IS NULL AND asset_subtype IS NOT NULL");
78+
// Capture only the IDs that are currently NULL so we restore exactly those rows
79+
final List<String> nullIds = new DotConnect()
80+
.setSQL("SELECT id FROM identifier WHERE base_type IS NULL AND asset_subtype IS NOT NULL")
81+
.loadObjectResults()
82+
.stream()
83+
.map(row -> (String) row.get("id"))
84+
.collect(Collectors.toList());
85+
86+
if (!nullIds.isEmpty()) {
87+
final String idList = nullIds.stream()
88+
.map(id -> "'" + id + "'")
89+
.collect(Collectors.joining(","));
90+
new DotConnect().executeStatement(
91+
"UPDATE identifier SET base_type = 1 WHERE id IN (" + idList + ")");
92+
}
7993

8094
try {
8195
assertFalse("forceRun() should return false when index exists and no pending rows remain",
8296
task.forceRun());
8397
} finally {
84-
new DotConnect().executeStatement(
85-
"UPDATE identifier SET base_type = NULL WHERE base_type = 1 AND asset_subtype IS NOT NULL");
98+
// Restore only the rows we modified
99+
if (!nullIds.isEmpty()) {
100+
final String idList = nullIds.stream()
101+
.map(id -> "'" + id + "'")
102+
.collect(Collectors.joining(","));
103+
new DotConnect().executeStatement(
104+
"UPDATE identifier SET base_type = NULL WHERE id IN (" + idList + ")");
105+
}
86106
}
87107
}
88108

0 commit comments

Comments
 (0)