/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.handler;
import java.io.File;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
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;
/**
* {@link PersistenceHandler} implementation which reads the configuration from XML files. These configuration is passed
* in {@link #initialize(Map)}
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class XmlPersistenceHandler implements PersistenceHandler {
protected static final Logger logger = LoggerFactory.getLogger(XmlPersistenceHandler.class);
private Map<String, User> userMap;
private Map<String, Role> roleMap;
private boolean userMapDirty;
private boolean roleMapDirty;
private Map<String, String> parameterMap;
private long usersFileDate;
private long rolesFileDate;
private File usersPath;
private File rolesPath;
@Override
public List<User> getAllUsers() {
synchronized (this.userMap) {
return new LinkedList<>(this.userMap.values());
}
}
@Override
public List<Role> getAllRoles() {
synchronized (this.roleMap) {
return new LinkedList<>(this.roleMap.values());
}
}
@Override
public User getUser(String username) {
return this.userMap.get(username);
}
@Override
public Role getRole(String roleName) {
return this.roleMap.get(roleName);
}
@Override
public User removeUser(String username) {
User user = this.userMap.remove(username);
this.userMapDirty = user != null;
return user;
}
@Override
public Role removeRole(String roleName) {
Role role = this.roleMap.remove(roleName);
this.roleMapDirty = role != null;
return role;
}
@Override
public void addUser(User user) {
if (this.userMap.containsKey(user.getUsername()))
throw new IllegalStateException(MessageFormat.format("The user {0} already exists!", user.getUsername()));
this.userMap.put(user.getUsername(), user);
this.userMapDirty = true;
}
@Override
public void replaceUser(User user) {
if (!this.userMap.containsKey(user.getUsername()))
throw new IllegalStateException(MessageFormat
.format("The user {0} can not be replaced as it does not exiset!", user.getUsername()));
this.userMap.put(user.getUsername(), user);
this.userMapDirty = true;
}
@Override
public void addRole(Role role) {
if (this.roleMap.containsKey(role.getName()))
throw new IllegalStateException(MessageFormat.format("The role {0} already exists!", role.getName()));
this.roleMap.put(role.getName(), role);
this.roleMapDirty = true;
}
@Override
public void replaceRole(Role role) {
if (!this.roleMap.containsKey(role.getName()))
throw new IllegalStateException(
MessageFormat.format("The role {0} can not be replaced as it does not exist!", role.getName()));
this.roleMap.put(role.getName(), role);
this.roleMapDirty = true;
}
/**
* Initializes this {@link XmlPersistenceHandler} by reading the following parameters:
* <ul>
* <li>{@link XmlConstants#XML_PARAM_BASE_PATH}</li>
* <li>{@link XmlConstants#XML_PARAM_MODEL_FILE}</li>
* </ul>
*/
@Override
public void initialize(Map<String, String> paramsMap) {
// copy parameter map
this.parameterMap = Collections.unmodifiableMap(new HashMap<>(paramsMap));
// get and validate base bath
String basePath = this.parameterMap.get(XmlConstants.XML_PARAM_BASE_PATH);
File basePathF = new File(basePath);
if (!basePathF.exists() && !basePathF.isDirectory()) {
String msg = "[{0}] Defined parameter {1} does not point to a valid path at {2}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, PersistenceHandler.class.getName(), XmlConstants.XML_PARAM_BASE_PATH,
basePathF.getAbsolutePath());
throw new PrivilegeException(msg);
}
// get users file name
String usersFileName = this.parameterMap.get(XmlConstants.XML_PARAM_USERS_FILE);
if (StringHelper.isEmpty(usersFileName)) {
String msg = "[{0}] Defined parameter {1} is not valid as it is empty!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, PersistenceHandler.class.getName(), XmlConstants.XML_PARAM_USERS_FILE);
throw new PrivilegeException(msg);
}
// get roles file name
String rolesFileName = this.parameterMap.get(XmlConstants.XML_PARAM_ROLES_FILE);
if (StringHelper.isEmpty(rolesFileName)) {
String msg = "[{0}] Defined parameter {1} is not valid as it is empty!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, PersistenceHandler.class.getName(), XmlConstants.XML_PARAM_ROLES_FILE);
throw new PrivilegeException(msg);
}
// validate users file exists
String usersPathS = basePath + "/" + usersFileName; //$NON-NLS-1$
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}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, PersistenceHandler.class.getName(), XmlConstants.XML_PARAM_USERS_FILE,
usersPath.getAbsolutePath());
throw new PrivilegeException(msg);
}
// validate roles file exists
String rolesPathS = basePath + "/" + rolesFileName; //$NON-NLS-1$
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}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, PersistenceHandler.class.getName(), XmlConstants.XML_PARAM_ROLES_FILE,
rolesPath.getAbsolutePath());
throw new PrivilegeException(msg);
}
// save path to model
this.usersPath = usersPath;
this.rolesPath = rolesPath;
if (reload())
logger.info("Privilege Data loaded."); //$NON-NLS-1$
}
/**
* Reads the XML configuration files which contain the model. Which configuration files are parsed was defined in
* the while calling {@link #initialize(Map)}
*
* @see #initialize(Map)
*/
@Override
public boolean reload() {
this.roleMap = Collections.synchronizedMap(new HashMap<String, Role>());
this.userMap = Collections.synchronizedMap(new HashMap<String, User>());
// parse models xml file to XML document
PrivilegeUsersSaxReader usersXmlHandler = new PrivilegeUsersSaxReader();
XmlHelper.parseDocument(this.usersPath, usersXmlHandler);
PrivilegeRolesSaxReader rolesXmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(this.rolesPath, rolesXmlHandler);
this.usersFileDate = this.usersPath.lastModified();
this.rolesFileDate = this.rolesPath.lastModified();
// ROLES
List<Role> roles = rolesXmlHandler.getRoles();
for (Role role : roles) {
this.roleMap.put(role.getName(), role);
}
// USERS
List<User> users = usersXmlHandler.getUsers();
for (User user : users) {
this.userMap.put(user.getUsername(), user);
}
this.userMapDirty = false;
this.roleMapDirty = false;
logger.info(MessageFormat.format("Read {0} Users", this.userMap.size())); //$NON-NLS-1$
logger.info(MessageFormat.format("Read {0} Roles", this.roleMap.size())); //$NON-NLS-1$
// validate referenced roles exist
for (User user : users) {
for (String roleName : user.getRoles()) {
// validate that role exists
if (getRole(roleName) == null) {
String msg = "Role {0} does not exist referenced by user {1}";
msg = MessageFormat.format(msg, roleName, user.getUsername());
throw new PrivilegeException(msg);
}
}
}
return true;
}
/**
* Writes the model to the XML files. Where the files are written to was defined in the {@link #initialize(Map)}
*/
@Override
public boolean persist() {
// get users file name
String usersFileName = this.parameterMap.get(XmlConstants.XML_PARAM_USERS_FILE);
if (usersFileName == null || usersFileName.isEmpty()) {
String msg = "[{0}] Defined parameter {1} is invalid"; //$NON-NLS-1$
msg = MessageFormat.format(msg, PersistenceHandler.class.getName(), XmlConstants.XML_PARAM_USERS_FILE);
throw new PrivilegeException(msg);
}
// get roles file name
String rolesFileName = this.parameterMap.get(XmlConstants.XML_PARAM_ROLES_FILE);
if (rolesFileName == null || rolesFileName.isEmpty()) {
String msg = "[{0}] Defined parameter {1} is invalid"; //$NON-NLS-1$
msg = MessageFormat.format(msg, PersistenceHandler.class.getName(), XmlConstants.XML_PARAM_ROLES_FILE);
throw new PrivilegeException(msg);
}
boolean saved = false;
// get users file
boolean usersFileUnchanged = this.usersPath.exists() && this.usersPath.lastModified() == this.usersFileDate;
if (usersFileUnchanged && !this.userMapDirty) {
logger.warn("Not persisting of users as current file is unchanged and users data is not dirty"); //$NON-NLS-1$
} else {
// delegate writing
PrivilegeUsersDomWriter modelWriter = new PrivilegeUsersDomWriter(getAllUsers(), this.usersPath);
modelWriter.write();
this.userMapDirty = false;
saved = true;
}
// get roles file
boolean rolesFileUnchanged = this.rolesPath.exists() && this.rolesPath.lastModified() == this.rolesFileDate;
if (rolesFileUnchanged && !this.roleMapDirty) {
logger.warn("Not persisting of roles as current file is unchanged and roles data is not dirty"); //$NON-NLS-1$
} else {
// delegate writing
PrivilegeRolesDomWriter modelWriter = new PrivilegeRolesDomWriter(getAllRoles(), this.rolesPath);
modelWriter.write();
this.roleMapDirty = false;
saved = true;
}
// reset dirty states
return saved;
}
}