Skip to content

Commit

Permalink
#27878 include in 23.10.24
Browse files Browse the repository at this point in the history
  • Loading branch information
erickgonzalez committed May 1, 2024
1 parent 0fc91fd commit 14ac963
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 89 deletions.
3 changes: 2 additions & 1 deletion dotCMS/hotfix_tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,5 @@ This maintenance release includes the following code fixes:
85. https://github.com/dotCMS/core/issues/27516 : Secrets can not find the inode on certain url #27516
86. https://github.com/dotCMS/core/issues/28110 : Only run startup tasks if they need to be run #28110
87. https://github.com/dotCMS/core/issues/28105 : PDF as Binary / upload field doesn't show preview #28105
88. https://github.com/dotCMS/core/issues/27563 : Site or Folder field does not show on the relate content window #27563
88. https://github.com/dotCMS/core/issues/27563 : Site or Folder field does not show on the relate content window #27563
89. https://github.com/dotCMS/core/issues/27878 : System Table Blocks on Load #27878
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,29 @@ public void test_crud_success () throws Throwable {
systemTableFactory.clearCache();
// SAVE + FIND
LocalTransaction.wrap(()->systemTableFactory.saveOrUpdate(key1, value1));
final Optional<String> value1FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
final Optional<Object> value1FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
Assert.assertTrue("Should return something", value1FromDB.isPresent());
Assert.assertEquals(
"The value previous added should be the same of the value recovery from the db with the key: " + key1,
value1, value1FromDB.get());

// UPDATE + FIND
LocalTransaction.wrap(()->systemTableFactory.saveOrUpdate(key1, value2));
final Optional<String> value2FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
final Optional<Object> value2FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
Assert.assertEquals(
"The value previous added should be the same of the value recovery from the db with the key: " + key1,
value2, value2FromDB.get());

// DELETE + FIND
LocalTransaction.wrap(()->systemTableFactory.delete(key1));
final Optional<String> value3FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
final Optional<Object> value3FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
Assert.assertFalse("Should not return something", value3FromDB.isPresent());
}
}


/**
* Method to test: test double save constraint {@link SystemTableFactory#save(String, String)}
* Method to test: test double save constraint {@link SystemTableFactory#saveOrUpdate(String, Object)}
* Given Scenario: Creates a key/value twice
* ExpectedResult: Should throw an exception b/c the key already exist
* @throws Throwable
Expand All @@ -99,15 +99,15 @@ public void test_double_insert () throws Throwable {
systemTableFactory.clearCache();
// SAVE + FIND
LocalTransaction.wrap(()->systemTableFactory.saveOrUpdate(key1, value1));
final Optional<String> value1FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
final Optional<Object> value1FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
Assert.assertTrue("Should return something", value1FromDB.isPresent());
Assert.assertEquals(
"The value previous added should be the same of the value recovery from the db with the key: " + key1,
value1, value1FromDB.get());

// this should be an update
LocalTransaction.wrap(()->systemTableFactory.saveOrUpdate(key1, value1));
final Optional<String> value2FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
final Optional<Object> value2FromDB = wrapOnReadOnlyConn(()->systemTableFactory.find(key1));
Assert.assertTrue("Should return something", value2FromDB.isPresent());
Assert.assertEquals(
"The value previous added should be the same of the value recovery from the db with the key: " + key1,
Expand Down Expand Up @@ -140,7 +140,7 @@ public void test_find_all () throws Throwable {
// SAVE + FIND
LocalTransaction.wrap(() -> systemTableFactory.saveOrUpdate(key1, value1));
LocalTransaction.wrap(() -> systemTableFactory.saveOrUpdate(key2, value2));
final Map<String, String> value1FromDB = wrapOnReadOnlyConn(() -> systemTableFactory.findAll());
final Map<String, Object> value1FromDB = wrapOnReadOnlyConn(() -> systemTableFactory.findAll());
Assert.assertTrue("Should has key1", value1FromDB.containsKey(key1));
Assert.assertTrue("Should has key2", value1FromDB.containsKey(key2));
Assert.assertEquals(
Expand Down
180 changes: 180 additions & 0 deletions dotCMS/src/main/java/com/dotcms/business/CacheableEagerFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package com.dotcms.business;

import com.dotmarketing.business.CachableSupport;
import com.dotmarketing.common.db.DotConnect;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.util.UtilMethods;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
* Provides an eager template for a key value table, this eager strategy basically loads all records in cache when any
* pair is requested, so tries to keeps all records in memory always.
* @param <K>
* @param <V>
*/
public interface CacheableEagerFactory<K, V> {

/**
* Returns the key used as a flag when the cache is loaded
* @return K
*/
K getLoadedKey();

/**
* Returns the value used as a flag when the cache is loaded
* @return V
*/
V getLoadedValue();

/**
* Returns the cache used to store the records
* @return
*/
CachableSupport<K, V> getCache();

/**
* Returns the SQL used to load all records
* @return
*/
String getSelectAllSQL();

/**
* Does the save or update operation
* @param key
* @param value
* @throws DotDataException
*/
void saveOrUpdateInternal(final K key, final V value) throws DotDataException;

/**
* Does the delete operation
* @param key
* @throws DotDataException
*/
void deleteInternal(final K key) throws DotDataException;

/**
* Returns the key from the record map
* @param recordMap
* @return K
*/
K getKey(Map<String, Object> recordMap);

/**
* Returns the value from the record map
* @param recordMap
* @return V
*/
V getValue(Map<String, Object> recordMap);

/**
* Wraps the value, does nothing by default but you can override it to do some transformation
* @param value V
* @return V
*/
default V wrap(final V value) {

return value;
}

/**
* Finds a record by key, returns optional empty if not found
* this method tries to find the key from the cache, if not found it tries to load all records in cache and then retries
* @param key
* @return Optional V
* @throws DotDataException
*/
default Optional<V> find(final K key) throws DotDataException {

V value = null;
if (UtilMethods.isSet(key)) {

value = this.getCache().get(key);
if (Objects.isNull(value) && Objects.isNull((getCache().get(this.getLoadedKey())))) {

this.findAll();
return find(key);
}
}

return Optional.ofNullable(wrap(value));
}

/**
* Finds all records and loads them into the cache
* @return Map<K, V>
* @throws DotDataException
*/
default Map<K, V> findAll() throws DotDataException {

final Map<K, V> records = new HashMap<>();

final List<Map<String, Object>> result = new DotConnect()
.setSQL(this.getSelectAllSQL())
.loadObjectResults();

for (final Map<String, Object> recordMap : result) {

final K key = getKey(recordMap);
final V value = getValue(recordMap);
if (Objects.nonNull(key) && Objects.nonNull(value)) {
records.put(key, value);
this.getCache().put(key, value);
}
}

// cache already load
this.getCache().put(getLoadedKey(), getLoadedValue());
return records;
}

/**
* Saves or updates a record
* @param key
* @param value
* @throws DotDataException
*/
default void saveOrUpdate(final K key, final V value) throws DotDataException {

if (Objects.nonNull(key) && Objects.nonNull(value)) {

saveOrUpdateInternal (key, value);

this.clearCache();
} else {

throw new DotDataException("The key and value should not be null");
}
}

/**
* Deletes a record
* @param key
* @throws DotDataException
*/
default void delete(final K key) throws DotDataException {

if (Objects.nonNull(key)) {

deleteInternal(key);
this.clearCache();
} else {

throw new DotDataException("The key should not be null");
}
}

/**
* Clears the cache
*/
default void clearCache() {

this.getCache().clearCache();
}

}
12 changes: 6 additions & 6 deletions dotCMS/src/main/java/com/dotcms/business/SystemTableFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,39 @@
* Encapsulates the persistance class for the System table
* @author jsanca
*/
public abstract class SystemTableFactory {
public interface SystemTableFactory {

/**
* Retrieve a value from the system table by key
* @param key {@link String}
* @throws DotDataException
*/
protected abstract Optional<String> find(String key) throws DotDataException;
Optional<Object> find(String key) throws DotDataException;

/**
* Retrieve all the values from the system table
* @return
* @throws DotDataException
*/
protected abstract Map<String, String> findAll() throws DotDataException;
Map<String, Object> findAll() throws DotDataException;

/**
* Save or Update a value in the system table
* @param key {@link String} key, should not exist
* @param value {@link String} value
* @throws DotDataException
*/
protected abstract void saveOrUpdate(String key, String value) throws DotDataException;
void saveOrUpdate(String key, Object value) throws DotDataException;

/**
* Deletes a value from the system table
* @param key {@link String} key, should exist
* @throws DotDataException
*/
protected abstract void delete(String key) throws DotDataException;
void delete(String key) throws DotDataException;

/**
* Clear the cache
*/
abstract protected void clearCache();
void clearCache();
}

0 comments on commit 14ac963

Please sign in to comment.