Skip to content

Commit

Permalink
unlock namespace when is redo operation
Browse files Browse the repository at this point in the history
  • Loading branch information
lepdou committed Mar 7, 2017
1 parent a002fa8 commit 9e51608
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
*/
@Aspect
@Component
public class NamespaceLockAspect {
private static final Logger logger = LoggerFactory.getLogger(NamespaceLockAspect.class);
public class NamespaceAcquireLockAspect {
private static final Logger logger = LoggerFactory.getLogger(NamespaceAcquireLockAspect.class);


@Autowired
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.ctrip.framework.apollo.adminservice.aop;


import com.google.common.collect.Maps;
import com.google.gson.Gson;

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.Release;
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.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.constants.GsonType;
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.core.utils.StringUtils;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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


/**
* unlock namespace if is redo operation.
* --------------------------------------------
* For example: If namespace has a item K1 = v1
* --------------------------------------------
* First operate: change k1 = v2 (lock namespace)
* Second operate: change k1 = v1 (unlock namespace)
*
*/
@Aspect
@Component
public class NamespaceUnlockAspect {

private Gson gson = new Gson();

@Autowired
private NamespaceLockService namespaceLockService;
@Autowired
private NamespaceService namespaceService;
@Autowired
private ItemService itemService;
@Autowired
private ReleaseService releaseService;
@Autowired
private BizConfig bizConfig;


//create item
@After("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, item, ..)")
public void requireLockAdvice(String appId, String clusterName, String namespaceName,
ItemDTO item) {
tryUnlock(namespaceService.findOne(appId, clusterName, namespaceName));
}

//update item
@After("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, itemId, item, ..)")
public void requireLockAdvice(String appId, String clusterName, String namespaceName, long itemId,
ItemDTO item) {
tryUnlock(namespaceService.findOne(appId, clusterName, namespaceName));
}

//update by change set
@After("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, changeSet, ..)")
public void requireLockAdvice(String appId, String clusterName, String namespaceName,
ItemChangeSets changeSet) {
tryUnlock(namespaceService.findOne(appId, clusterName, namespaceName));
}

//delete item
@After("@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.");
}
tryUnlock(namespaceService.findOne(item.getNamespaceId()));
}

private void tryUnlock(Namespace namespace) {
if (bizConfig.isNamespaceLockSwitchOff()) {
return;
}

if (!isModified(namespace)) {
namespaceLockService.unlock(namespace.getId());
}

}

boolean isModified(Namespace namespace) {
Release release = releaseService.findLatestActiveRelease(namespace);
List<Item> items = itemService.findItems(namespace.getId());

if (release == null) {
return hasNormalItems(items);
}

Map<String, String> releasedConfiguration = gson.fromJson(release.getConfigurations(), GsonType.CONFIG);
Map<String, String> configurationFromItems = Maps.newHashMap();

for (Item item : items) {
String key = item.getKey();
if (StringUtils.isBlank(key)) {
continue;
}
//added
if (releasedConfiguration.get(key) == null) {
return true;
}
configurationFromItems.put(key, item.getValue());
}

for (Map.Entry<String, String> entry : releasedConfiguration.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();

//deleted or modified
if (!Objects.equals(configurationFromItems.get(key), value)) {
return true;
}

}

return false;
}

private boolean hasNormalItems(List<Item> items) {
for (Item item : items) {
if (!StringUtils.isEmpty(item.getKey())) {
return true;
}
}

return false;
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ctrip.framework.apollo.adminservice;

import com.ctrip.framework.apollo.adminservice.aop.NamespaceLockTest;
import com.ctrip.framework.apollo.adminservice.aop.NamespaceUnlockAspectTest;
import com.ctrip.framework.apollo.adminservice.controller.AppControllerTest;
import com.ctrip.framework.apollo.adminservice.controller.AppNamespaceControllerTest;
import com.ctrip.framework.apollo.adminservice.controller.ControllerExceptionTest;
Expand All @@ -17,7 +18,8 @@
@SuiteClasses({
AppControllerTest.class, ReleaseControllerTest.class, ItemSetControllerTest.class,
ControllerExceptionTest.class, ControllerIntegrationExceptionTest.class,
NamespaceLockTest.class, InstanceConfigControllerTest.class, AppNamespaceControllerTest.class
NamespaceLockTest.class, InstanceConfigControllerTest.class, AppNamespaceControllerTest.class,
NamespaceUnlockAspectTest.class
})
public class AllTests {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class NamespaceLockTest {
@Mock
private BizConfig bizConfig;
@InjectMocks
NamespaceLockAspect namespaceLockAspect;
NamespaceAcquireLockAspect namespaceLockAspect;

@Test
public void acquireLockWithNotLockedAndSwitchON() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.ctrip.framework.apollo.adminservice.aop;

import com.ctrip.framework.apollo.biz.entity.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.service.ItemService;
import com.ctrip.framework.apollo.biz.service.ReleaseService;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class NamespaceUnlockAspectTest {

@Mock
private ReleaseService releaseService;
@Mock
private ItemService itemService;

@InjectMocks
private NamespaceUnlockAspect namespaceUnlockAspect;


@Test
public void testNamespaceHasNoNormalItemsAndRelease() {

long namespaceId = 1;
Namespace namespace = createNamespace(namespaceId);

when(releaseService.findLatestActiveRelease(namespace)).thenReturn(null);
when(itemService.findItems(namespaceId)).thenReturn(Collections.singletonList(createItem("", "")));

boolean isModified = namespaceUnlockAspect.isModified(namespace);

Assert.assertFalse(isModified);
}

@Test
public void testNamespaceAddItem() {
long namespaceId = 1;
Namespace namespace = createNamespace(namespaceId);

Release release = createRelease("{\"k1\":\"v1\"}");
List<Item> items = Arrays.asList(createItem("k1", "v1"), createItem("k2", "v2"));

when(releaseService.findLatestActiveRelease(namespace)).thenReturn(release);
when(itemService.findItems(namespaceId)).thenReturn(items);

boolean isModified = namespaceUnlockAspect.isModified(namespace);

Assert.assertTrue(isModified);
}

@Test
public void testNamespaceModifyItem() {
long namespaceId = 1;
Namespace namespace = createNamespace(namespaceId);

Release release = createRelease("{\"k1\":\"v1\"}");
List<Item> items = Arrays.asList(createItem("k1", "v2"));

when(releaseService.findLatestActiveRelease(namespace)).thenReturn(release);
when(itemService.findItems(namespaceId)).thenReturn(items);

boolean isModified = namespaceUnlockAspect.isModified(namespace);

Assert.assertTrue(isModified);
}

@Test
public void testNamespaceDeleteItem() {
long namespaceId = 1;
Namespace namespace = createNamespace(namespaceId);

Release release = createRelease("{\"k1\":\"v1\"}");
List<Item> items = Arrays.asList(createItem("k2", "v2"));

when(releaseService.findLatestActiveRelease(namespace)).thenReturn(release);
when(itemService.findItems(namespaceId)).thenReturn(items);

boolean isModified = namespaceUnlockAspect.isModified(namespace);

Assert.assertTrue(isModified);
}

private Namespace createNamespace(long namespaceId) {
Namespace namespace = new Namespace();
namespace.setId(namespaceId);
return namespace;
}

private Item createItem(String key, String value) {
Item item = new Item();
item.setKey(key);
item.setValue(value);
return item;
}

private Release createRelease(String configuration) {
Release release = new Release();
release.setConfigurations(configuration);
return release;
}

}

0 comments on commit 9e51608

Please sign in to comment.