bug 12399: introduced periodic thread that expires Project invitations

status 12399: resolved fixed
This commit is contained in:
Alena Prokharchyk 2011-12-09 12:20:06 -08:00
parent 21c2979341
commit 0731dac370
5 changed files with 123 additions and 56 deletions

View File

@ -970,14 +970,18 @@ public class ApiServer implements HttpRequestHandler {
if (errorCode == BaseCmd.UNSUPPORTED_ACTION_ERROR || apiCommandParams == null || apiCommandParams.isEmpty()) {
responseName = "errorresponse";
} else {
String cmdName = ((String[]) apiCommandParams.get("command"))[0];
cmdClassName = _apiCommands.getProperty(cmdName);
if (cmdClassName != null) {
Class<?> claz = Class.forName(cmdClassName);
responseName = ((BaseCmd) claz.newInstance()).getCommandName();
} else {
responseName = "errorresponse";
}
Object cmdObj = apiCommandParams.get("command");
//cmd name can be null when "command" parameter is missing in the request
if (cmdObj != null) {
String cmdName = ((String[])cmdObj) [0];
cmdClassName = _apiCommands.getProperty(cmdName);
if (cmdClassName != null) {
Class<?> claz = Class.forName(cmdClassName);
responseName = ((BaseCmd) claz.newInstance()).getCommandName();
} else {
responseName = "errorresponse";
}
}
}
ExceptionResponse apiResponse = new ExceptionResponse();

View File

@ -18,11 +18,15 @@
package com.cloud.projects;
import java.io.UnsupportedEncodingException;
import java.sql.Date;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.TimeZone;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.ejb.Local;
import javax.mail.Authenticator;
@ -63,9 +67,11 @@ import com.cloud.user.DomainManager;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.UserContext;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.Inject;
import com.cloud.utils.component.Manager;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.JoinBuilder;
@ -106,8 +112,10 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
private ProjectInvitationDao _projectInvitationDao;
protected boolean _invitationRequired = false;
protected long _invitationTimeOut = 86400;
protected long _invitationTimeOut = 86400000;
protected boolean _allowUserToCreateProjet = true;
protected ScheduledExecutorService _executor;
protected int _projectCleanupExpInvInterval = 60; //Interval defining how often project invitation cleanup thread is running
@Override
@ -116,7 +124,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
Map<String, String> configs = _configDao.getConfiguration(params);
_invitationRequired = Boolean.valueOf(configs.get(Config.ProjectInviteRequired.key()));
_invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key()));
_invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key()))*1000;
_allowUserToCreateProjet = Boolean.valueOf(configs.get(Config.AllowUserToCreateProject.key()));
@ -136,12 +144,14 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
}
_emailInvite = new EmailInvite(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug);
_executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Project-ExpireInvitations"));
return true;
}
@Override
public boolean start() {
_executor.scheduleWithFixedDelay(new ExpiredInvitationsCleanup(), _projectCleanupExpInvInterval, _projectCleanupExpInvInterval, TimeUnit.SECONDS);
return true;
}
@ -671,36 +681,54 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
return _projectAccountDao.search(sc, searchFilter);
}
public ProjectInvitation createAccountInvitation(Project project, Long accountId) {
//verify if the invitation was already generated
ProjectInvitationVO invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, project.getId());
public ProjectInvitation createAccountInvitation(Project project, Long accountId) {
if (activeInviteExists(project, accountId, null)) {
throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + project);
}
ProjectInvitation invitation= _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), accountId, project.getDomainId(), null, null));
return invitation;
}
@DB
public boolean activeInviteExists(Project project, Long accountId, String email) {
Transaction txn = Transaction.currentTxn();
txn.start();
//verify if the invitation was already generated
ProjectInvitationVO invite = null;
if (accountId != null) {
invite = _projectInvitationDao.findByAccountIdProjectId(accountId, project.getId());
} else if (email != null) {
invite = _projectInvitationDao.findByEmailAndProjectId(email, project.getId());
}
if (invite != null) {
if (_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) {
throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + project);
if (invite.getState() == ProjectInvitation.State.Completed || _projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) {
return true;
} else {
if (invite.getState() == ProjectInvitation.State.Pending) {
expireInvitation(invite);
}
//remove the expired/declined invitation
if (accountId != null) {
s_logger.debug("Removing invitation in state " + invite.getState() + " for account id=" + accountId + " to project " + project);
} else if (email != null) {
s_logger.debug("Removing invitation in state " + invite.getState() + " for email " + email + " to project " + project);
}
_projectInvitationDao.expunge(invite.getId());
}
}
return _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), accountId, project.getDomainId(), null, null));
}
txn.commit();
return false;
}
public ProjectInvitation generateTokenBasedInvitation(Project project, String email, String token) {
//verify if the invitation was already generated
ProjectInvitationVO invite = _projectInvitationDao.findPendingByEmailAndProjectId(email, project.getId());
if (invite != null) {
if (_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) {
throw new InvalidParameterValueException("There is already a pending invitation for email=" + email + " to the project id=" + project);
} else {
if (invite.getState() == ProjectInvitation.State.Pending) {
expireInvitation(invite);
}
}
}
if (activeInviteExists(project, null, email)) {
throw new InvalidParameterValueException("There is already a pending invitation for email " + email + " to the project id=" + project);
}
ProjectInvitation projectInvitation = _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), null, project.getDomainId(), email, token));
try {
@ -794,7 +822,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
if (activeOnly) {
sc.setParameters("state", ProjectInvitation.State.Pending);
sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - _invitationTimeOut));
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime()) - _invitationTimeOut));
}
return _projectInvitationDao.search(sc, searchFilter);
@ -837,9 +865,9 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
//check that invitation exists
ProjectInvitationVO invite = null;
if (token == null) {
invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, projectId);
invite = _projectInvitationDao.findByAccountIdProjectId(accountId, projectId, ProjectInvitation.State.Pending);
} else {
invite = _projectInvitationDao.findPendingByTokenAndProjectId(token, projectId);
invite = _projectInvitationDao.findPendingByTokenAndProjectId(token, projectId, ProjectInvitation.State.Pending);
}
if (invite != null) {
@ -1034,7 +1062,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
msg.setFrom(new InternetAddress(_emailSender, _emailSender));
msg.addRecipient(RecipientType.TO, address);
msg.setSubject("You are invited to join the cloud stack project id=" + projectId);
msg.setSentDate(new Date(System.currentTimeMillis() >> 10));
msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
msg.setContent(content, "text/plain");
msg.saveChanges();
@ -1076,4 +1104,24 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
return false;
}
}
public class ExpiredInvitationsCleanup implements Runnable {
@Override
public void run() {
try {
TimeZone.getDefault();
List<ProjectInvitationVO> invitationsToExpire = _projectInvitationDao.listInvitationsToExpire(_invitationTimeOut);
if (!invitationsToExpire.isEmpty()) {
s_logger.debug("Found " + invitationsToExpire.size() + " projects to expire");
for (ProjectInvitationVO invitationToExpire : invitationsToExpire) {
invitationToExpire.setState(ProjectInvitation.State.Expired);
_projectInvitationDao.update(invitationToExpire.getId(), invitationToExpire);
s_logger.trace("Expired project invitation id=" + invitationToExpire.getId());
}
}
} catch (Exception ex) {
s_logger.warn("Exception while running expired invitations cleanup", ex);
}
}
}
}

View File

@ -19,16 +19,18 @@ package com.cloud.projects.dao;
import java.util.List;
import com.cloud.projects.ProjectInvitation.State;
import com.cloud.projects.ProjectInvitationVO;
import com.cloud.utils.db.GenericDao;
public interface ProjectInvitationDao extends GenericDao<ProjectInvitationVO, Long>{
ProjectInvitationVO findPendingByAccountIdProjectId(long accountId, long projectId);
ProjectInvitationVO findByAccountIdProjectId(long accountId, long projectId, State... inviteState);
List<ProjectInvitationVO> listExpiredInvitations();
boolean expirePendingInvitations(long timeOut);
boolean isActive(long id, long timeout);
ProjectInvitationVO findPendingByEmailAndProjectId(String email, long projectId);
ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId);
ProjectInvitationVO findByEmailAndProjectId(String email, long projectId, State... inviteState);
ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId, State... inviteState);
void cleanupInvitations(long projectId);
ProjectInvitationVO findPendingById(long id);
List<ProjectInvitationVO> listInvitationsToExpire(long timeOut);
}

View File

@ -9,6 +9,7 @@ import org.apache.log4j.Logger;
import com.cloud.projects.ProjectInvitation.State;
import com.cloud.projects.ProjectInvitationVO;
import com.cloud.utils.DateUtil;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@ -25,7 +26,7 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
AllFieldsSearch.and("projectId", AllFieldsSearch.entity().getProjectId(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("created", AllFieldsSearch.entity().getCreated(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("projectAccountId", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.IN);
AllFieldsSearch.and("email", AllFieldsSearch.entity().getEmail(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("token", AllFieldsSearch.entity().getToken(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), SearchCriteria.Op.EQ);
@ -42,12 +43,14 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
@Override
public ProjectInvitationVO findPendingByAccountIdProjectId(long accountId, long projectId) {
public ProjectInvitationVO findByAccountIdProjectId(long accountId, long projectId, State... inviteState) {
SearchCriteria<ProjectInvitationVO> sc = AllFieldsSearch.create();
sc.setParameters("accountId", accountId);
sc.setParameters("projectId", projectId);
sc.setParameters("state", State.Pending);
if (inviteState != null && inviteState.length > 0) {
sc.setParameters("state", (Object[])inviteState);
}
return findOneBy(sc);
}
@ -64,7 +67,7 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
boolean success = true;
SearchCriteria<ProjectInvitationVO> sc = InactiveSearch.create();
sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - timeout));
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime() >> 10) - timeout));
sc.setParameters("state", State.Pending);
List<ProjectInvitationVO> invitationsToExpire = listBy(sc);
@ -75,10 +78,17 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
success = false;
}
}
return success;
}
@Override
public List<ProjectInvitationVO> listInvitationsToExpire (long timeOut) {
SearchCriteria<ProjectInvitationVO> sc = InactiveSearch.create();
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime()) - timeOut));
sc.setParameters("state", State.Pending);
return listBy(sc);
}
@Override
public boolean isActive(long id, long timeout) {
SearchCriteria<ProjectInvitationVO> sc = InactiveSearch.create();
@ -90,7 +100,7 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
return false;
}
sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - timeout));
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime()) - timeout));
if (findOneBy(sc) == null) {
return true;
@ -100,21 +110,25 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
}
@Override
public ProjectInvitationVO findPendingByEmailAndProjectId(String email, long projectId) {
public ProjectInvitationVO findByEmailAndProjectId(String email, long projectId, State... inviteState) {
SearchCriteria<ProjectInvitationVO> sc = AllFieldsSearch.create();
sc.setParameters("email", email);
sc.setParameters("projectId", projectId);
sc.setParameters("state", State.Pending);
if (inviteState != null && inviteState.length > 0) {
sc.setParameters("state", (Object[])inviteState);
}
return findOneBy(sc);
}
@Override
public ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId) {
public ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId, State... inviteState) {
SearchCriteria<ProjectInvitationVO> sc = AllFieldsSearch.create();
sc.setParameters("token", token);
sc.setParameters("projectId", projectId);
sc.setParameters("state", State.Pending);
if (inviteState != null && inviteState.length > 0) {
sc.setParameters("state", (Object[])inviteState);
}
return findOneBy(sc);
}

View File

@ -18,14 +18,13 @@
package com.cloud.utils;
import java.net.URI;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import com.cloud.utils.exception.CloudRuntimeException;
public class DateUtil {