diff --git a/privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java b/privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java index 8676b88bc..64a251010 100644 --- a/privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java +++ b/privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java @@ -147,6 +147,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { private ElementLockingHandler lockingHandler; private ScheduledExecutorService executorService; private Future persistSessionsTask; + private Future persistModelTask; @Override public SingleSignOnHandler getSsoHandler() { @@ -395,10 +396,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { prvCtx.assertHasPrivilege(PRIVILEGE_ADD_USER); // make sure userId is not set - if (isNotEmpty(userRepParam.getUserId())) { - String msg = "UserId can not be set when adding a new user!"; - throw new PrivilegeModelException(format(msg, userRepParam.getUsername())); - } + if (isNotEmpty(userRepParam.getUserId())) + throw new PrivilegeModelException("UserId can not be set when adding a new user!"); UserRep userRep = userRepParam.getCopy(); @@ -443,6 +442,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate to persistence handler this.persistenceHandler.addUser(newUser); + persistModelAsync(); logger.info("Created new user " + newUser.getUsername()); @@ -527,6 +527,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate to persistence handler toCreate.forEach(user -> this.persistenceHandler.addUser(user)); toUpdate.forEach(user -> this.persistenceHandler.replaceUser(user)); + persistModelAsync(); logger.info("Created " + toCreate.size() + " users"); logger.info("Updated " + toUpdate.size() + " users"); @@ -590,6 +591,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate to persistence handler this.persistenceHandler.replaceUser(newUser); + persistModelAsync(); logger.info("Replaced user " + newUser.getUsername()); @@ -686,6 +688,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate to persistence handler this.persistenceHandler.replaceUser(newUser); + persistModelAsync(); logger.info("Updated user " + newUser.getUsername()); @@ -719,6 +722,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user removal to persistence handler invalidSessionsFor(existingUser); this.persistenceHandler.removeUser(username); + persistModelAsync(); logger.info("Removed user " + username); @@ -772,6 +776,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user replacement to persistence handler this.persistenceHandler.replaceUser(newUser); + persistModelAsync(); logger.info("Added role " + roleName + " to " + newUser.getUsername()); @@ -819,6 +824,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user replacement to persistence handler this.persistenceHandler.replaceUser(newUser); + persistModelAsync(); logger.info("Removed role " + roleName + " from " + newUser.getUsername()); @@ -858,11 +864,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user replacement to persistence handler this.persistenceHandler.replaceUser(newUser); - - // perform automatic persisting, if enabled - if (this.autoPersistOnUserChangesData) { - this.persistenceHandler.persist(); - } + persistModelAsync(); logger.info("Set locale to " + locale + " for " + newUser.getUsername()); @@ -896,11 +898,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user replacement to persistence handler this.persistenceHandler.replaceUser(newUser); - - // perform automatic persisting, if enabled - if (this.autoPersistOnUserChangesData) { - this.persistenceHandler.persist(); - } + persistModelAsync(); logger.info("Requiring user " + newUser.getUsername() + " to change their password on next login."); } @@ -956,15 +954,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user replacement to persistence handler this.persistenceHandler.replaceUser(newUser); + persistModelAsync(); - // perform automatic persisting, if enabled - if (this.autoPersistOnUserChangesData) { - this.persistenceHandler.persist(); - } - - if (certificate.getUsage() == Usage.SET_PASSWORD) { + if (certificate.getUsage() == Usage.SET_PASSWORD) invalidate(certificate); - } if (password == null) logger.info("Cleared password for " + newUser.getUsername()); @@ -1004,6 +997,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user replacement to persistence handler this.persistenceHandler.replaceUser(newUser); + persistModelAsync(); logger.info("Set state of user " + newUser.getUsername() + " to " + state); @@ -1042,6 +1036,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate to persistence handler this.persistenceHandler.addRole(newRole); + persistModelAsync(); logger.info("Added new role " + newRole.getName()); @@ -1084,6 +1079,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate to persistence handler this.persistenceHandler.replaceRole(newRole); + persistModelAsync(); logger.info("Replaced role " + newRole.getName()); @@ -1127,6 +1123,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate role removal to persistence handler this.persistenceHandler.removeRole(roleName); + persistModelAsync(); logger.info("Removed role " + roleName); @@ -1189,6 +1186,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate role replacement to persistence handler this.persistenceHandler.replaceRole(newRole); + persistModelAsync(); logger.info("Added/replaced privilege " + privilegeRep.getName() + " to " + roleName); @@ -1239,6 +1237,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // delegate user replacement to persistence handler this.persistenceHandler.replaceRole(newRole); + persistModelAsync(); logger.info("Removed privilege " + privilegeName + " from " + roleName); @@ -1264,6 +1263,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { this.privilegeContextMap.put(cert.getSessionId(), privilegeContext); } } + + persistSessionsAsync(); } /** @@ -1286,6 +1287,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { this.privilegeContextMap.put(cert.getSessionId(), privilegeContext); } } + + persistSessionsAsync(); } private void invalidSessionsFor(User user) { @@ -1360,7 +1363,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { " to " + source); } - persistSessions(); + persistSessionsAsync(); logger.info(format("Challenge validated for user {0} with usage {1}", username, usage)); return certificate; @@ -1418,15 +1421,14 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user); this.privilegeContextMap.put(sessionId, privilegeContext); - persistSessions(); + persistSessionsAsync(); // save last login if (user.getHistory().isFirstLoginEmpty()) user.getHistory().setFirstLogin(ZonedDateTime.now()); user.getHistory().setLastLogin(ZonedDateTime.now()); this.persistenceHandler.replaceUser(user); - if (this.autoPersistOnUserChangesData) - this.persistenceHandler.persist(); + persistModelAsync(); // log logger.info(format("User {0} authenticated: {1}", username, certificate)); @@ -1478,9 +1480,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { user.getHistory().setFirstLogin(internalUser.getHistory().getFirstLogin()); this.persistenceHandler.replaceUser(user); } - - if (this.autoPersistOnUserChangesData) - this.persistenceHandler.persist(); + persistModelAsync(); // get 2 auth tokens String authToken = this.encryptionHandler.nextToken(); @@ -1495,7 +1495,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { PrivilegeContext privilegeContext = buildPrivilegeContext(certificate, user); this.privilegeContextMap.put(sessionId, privilegeContext); - persistSessions(); + persistSessionsAsync(); // log logger.info(format("User {0} authenticated: {1}", user.getUsername(), certificate)); @@ -1546,8 +1546,6 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // invalidate the previous session invalidate(certificate); - persistSessions(); - // log logger.info(format("User {0} refreshed session: {1}", user.getUsername(), refreshedCert)); @@ -1573,7 +1571,24 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { user.getLocale(), userRoles, new HashMap<>(user.getProperties())); } - private boolean persistSessions() { + private synchronized void persistModelAsync() { + if (!this.autoPersistOnUserChangesData) + return; + + // async execution, max. once per second + if (this.persistModelTask != null) + this.persistModelTask.cancel(true); + this.persistModelTask = this.executorService.schedule( + () -> this.lockingHandler.lockedExecute("persist-model", () -> { + try { + this.persistenceHandler.persist(); + } catch (Exception e) { + logger.error("Failed to persist model!", e); + } + }), 1, TimeUnit.SECONDS); + } + + private synchronized boolean persistSessionsAsync() { if (!this.persistSessions) return false; @@ -1759,8 +1774,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { user.isPasswordChangeRequested(), user.getHistory().getClone()); // delegate user replacement to persistence handler - this.persistenceHandler.replaceUser(newUser); this.persistenceHandler.replaceUser(user); + persistModelAsync(); logger.info("Updated password for " + user.getUsername()); } @@ -1854,7 +1869,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { // persist sessions if (privilegeContext != null) - persistSessions(); + persistSessionsAsync(); // return true if object was really removed boolean loggedOut = privilegeContext != null; @@ -1970,6 +1985,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { PrivilegeContext prvCtx = validate(certificate); prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_PERSIST)); + // persist non async return this.persistenceHandler.persist(); } @@ -1980,7 +1996,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { PrivilegeContext prvCtx = validate(certificate); prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_PERSIST_SESSIONS)); - return persistSessions(); + return persistSessionsAsync(); } @Override diff --git a/privilege/src/main/java/li/strolch/privilege/handler/XmlPersistenceHandler.java b/privilege/src/main/java/li/strolch/privilege/handler/XmlPersistenceHandler.java index 88bd6b827..b0bb92f76 100644 --- a/privilege/src/main/java/li/strolch/privilege/handler/XmlPersistenceHandler.java +++ b/privilege/src/main/java/li/strolch/privilege/handler/XmlPersistenceHandler.java @@ -15,17 +15,6 @@ */ package li.strolch.privilege.handler; -import static java.text.MessageFormat.format; -import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_CASE_INSENSITIVE_USERNAME; -import static li.strolch.privilege.helper.XmlConstants.*; -import static li.strolch.utils.helper.StringHelper.formatNanoDuration; - -import java.io.File; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.helper.XmlConstants; import li.strolch.privilege.model.internal.Role; @@ -34,11 +23,22 @@ import li.strolch.privilege.xml.PrivilegeRolesDomWriter; import li.strolch.privilege.xml.PrivilegeRolesSaxReader; import li.strolch.privilege.xml.PrivilegeUsersDomWriter; import li.strolch.privilege.xml.PrivilegeUsersSaxReader; -import li.strolch.utils.helper.StringHelper; import li.strolch.utils.helper.XmlHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static java.text.MessageFormat.format; +import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_CASE_INSENSITIVE_USERNAME; +import static li.strolch.privilege.helper.XmlConstants.*; +import static li.strolch.utils.helper.StringHelper.formatNanoDuration; +import static li.strolch.utils.helper.StringHelper.isEmpty; + /** * {@link PersistenceHandler} implementation which reads the configuration from XML files. These configuration is passed * in {@link #initialize(Map)} @@ -167,39 +167,11 @@ public class XmlPersistenceHandler implements PersistenceHandler { throw new PrivilegeException(msg); } - // get users file name - String usersFileName = this.parameterMap.get(XML_PARAM_USERS_FILE); - if (StringHelper.isEmpty(usersFileName)) { - String msg = "[{0}] Defined parameter {1} is not valid as it is empty!"; - msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_USERS_FILE); - throw new PrivilegeException(msg); - } + // get users file path + File usersPath = getFile(basePath, XML_PARAM_USERS_FILE); - // get roles file name - String rolesFileName = this.parameterMap.get(XML_PARAM_ROLES_FILE); - if (StringHelper.isEmpty(rolesFileName)) { - String msg = "[{0}] Defined parameter {1} is not valid as it is empty!"; - msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_ROLES_FILE); - throw new PrivilegeException(msg); - } - - // validate users file exists - String usersPathS = basePath + "/" + usersFileName; - File usersPath = new File(usersPathS); - if (!usersPath.exists()) { - String msg = "[{0}] Defined parameter {1} is invalid as users file does not exist at path {2}"; - msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_USERS_FILE, usersPath.getAbsolutePath()); - throw new PrivilegeException(msg); - } - - // validate roles file exists - String rolesPathS = basePath + "/" + rolesFileName; - File rolesPath = new File(rolesPathS); - if (!rolesPath.exists()) { - String msg = "[{0}] Defined parameter {1} is invalid as roles file does not exist at path {2}"; - msg = format(msg, PersistenceHandler.class.getName(), XML_PARAM_ROLES_FILE, rolesPath.getAbsolutePath()); - throw new PrivilegeException(msg); - } + // get roles file path + File rolesPath = getFile(basePath, XML_PARAM_ROLES_FILE); // save path to model this.usersPath = usersPath; @@ -211,6 +183,25 @@ public class XmlPersistenceHandler implements PersistenceHandler { logger.info("Privilege Data loaded."); } + private File getFile(String basePath, String param) { + String fileName = this.parameterMap.get(param); + if (isEmpty(fileName)) { + String msg = "[{0}] Defined parameter {1} is not valid as it is empty!"; + msg = format(msg, PersistenceHandler.class.getName(), param); + throw new PrivilegeException(msg); + } + + String path = basePath + "/" + fileName; + File file = new File(path); + if (!file.exists()) { + String msg = "[{0}] Defined parameter {1} is invalid as file does not exist at path {2}"; + msg = format(msg, PersistenceHandler.class.getName(), param, file.getAbsolutePath()); + throw new PrivilegeException(msg); + } + + return file; + } + /** * Reads the XML configuration files which contain the model. Which configuration files are parsed was defined in * the while calling {@link #initialize(Map)}