package com.ctrip.framework.apollo.adminservice.aop; import com.ctrip.framework.apollo.biz.config.BizConfig; import com.ctrip.framework.apollo.biz.entity.Item; import com.ctrip.framework.apollo.biz.entity.Namespace; import com.ctrip.framework.apollo.biz.entity.NamespaceLock; import com.ctrip.framework.apollo.biz.service.ItemService; import com.ctrip.framework.apollo.biz.service.NamespaceLockService; import com.ctrip.framework.apollo.biz.service.NamespaceService; import com.ctrip.framework.apollo.common.dto.ItemChangeSets; import com.ctrip.framework.apollo.common.dto.ItemDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.exception.ServiceException; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Component; /** * 一个namespace在一次发布中只能允许一个人修改配置 * 通过数据库lock表来实现 */ @Aspect @Component public class NamespaceAcquireLockAspect { private static final Logger logger = LoggerFactory.getLogger(NamespaceAcquireLockAspect.class); @Autowired private NamespaceLockService namespaceLockService; @Autowired private NamespaceService namespaceService; @Autowired private ItemService itemService; @Autowired private BizConfig bizConfig; //create item @Before("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, item, ..)") public void requireLockAdvice(String appId, String clusterName, String namespaceName, ItemDTO item) { acquireLock(appId, clusterName, namespaceName, item.getDataChangeLastModifiedBy()); } //update item @Before("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, itemId, item, ..)") public void requireLockAdvice(String appId, String clusterName, String namespaceName, long itemId, ItemDTO item) { acquireLock(appId, clusterName, namespaceName, item.getDataChangeLastModifiedBy()); } //update by change set @Before("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, changeSet, ..)") public void requireLockAdvice(String appId, String clusterName, String namespaceName, ItemChangeSets changeSet) { acquireLock(appId, clusterName, namespaceName, changeSet.getDataChangeLastModifiedBy()); } //delete item @Before("@annotation(PreAcquireNamespaceLock) && args(itemId, operator, ..)") public void requireLockAdvice(long itemId, String operator) { Item item = itemService.findOne(itemId); if (item == null){ throw new BadRequestException("item not exist."); } acquireLock(item.getNamespaceId(), operator); } void acquireLock(String appId, String clusterName, String namespaceName, String currentUser) { if (bizConfig.isNamespaceLockSwitchOff()) { return; } Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName); acquireLock(namespace, currentUser); } void acquireLock(long namespaceId, String currentUser) { if (bizConfig.isNamespaceLockSwitchOff()) { return; } Namespace namespace = namespaceService.findOne(namespaceId); acquireLock(namespace, currentUser); } private void acquireLock(Namespace namespace, String currentUser) { if (namespace == null) { throw new BadRequestException("namespace not exist."); } long namespaceId = namespace.getId(); NamespaceLock namespaceLock = namespaceLockService.findLock(namespaceId); if (namespaceLock == null) { try { tryLock(namespaceId, currentUser); //lock success } catch (DataIntegrityViolationException e) { //lock fail namespaceLock = namespaceLockService.findLock(namespaceId); checkLock(namespace, namespaceLock, currentUser); } catch (Exception e) { logger.error("try lock error", e); throw e; } } else { //check lock owner is current user checkLock(namespace, namespaceLock, currentUser); } } private void tryLock(long namespaceId, String user) { NamespaceLock lock = new NamespaceLock(); lock.setNamespaceId(namespaceId); lock.setDataChangeCreatedBy(user); lock.setDataChangeLastModifiedBy(user); namespaceLockService.tryLock(lock); } private void checkLock(Namespace namespace, NamespaceLock namespaceLock, String currentUser) { if (namespaceLock == null) { throw new ServiceException( String.format("Check lock for %s failed, please retry.", namespace.getNamespaceName())); } String lockOwner = namespaceLock.getDataChangeCreatedBy(); if (!lockOwner.equals(currentUser)) { throw new BadRequestException( "namespace:" + namespace.getNamespaceName() + " is modified by " + lockOwner); } } }