Skip to content

Commit 20b5b8e

Browse files
authored
Merge pull request apolloconfig#557 from lepdou/lock
unlock namespace when is redo operation
2 parents a002fa8 + 9e51608 commit 20b5b8e

File tree

5 files changed

+268
-4
lines changed

5 files changed

+268
-4
lines changed

apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/aop/NamespaceLockAspect.java renamed to apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/aop/NamespaceAcquireLockAspect.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
*/
2929
@Aspect
3030
@Component
31-
public class NamespaceLockAspect {
32-
private static final Logger logger = LoggerFactory.getLogger(NamespaceLockAspect.class);
31+
public class NamespaceAcquireLockAspect {
32+
private static final Logger logger = LoggerFactory.getLogger(NamespaceAcquireLockAspect.class);
3333

3434

3535
@Autowired
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package com.ctrip.framework.apollo.adminservice.aop;
2+
3+
4+
import com.google.common.collect.Maps;
5+
import com.google.gson.Gson;
6+
7+
import com.ctrip.framework.apollo.biz.config.BizConfig;
8+
import com.ctrip.framework.apollo.biz.entity.Item;
9+
import com.ctrip.framework.apollo.biz.entity.Namespace;
10+
import com.ctrip.framework.apollo.biz.entity.Release;
11+
import com.ctrip.framework.apollo.biz.service.ItemService;
12+
import com.ctrip.framework.apollo.biz.service.NamespaceLockService;
13+
import com.ctrip.framework.apollo.biz.service.NamespaceService;
14+
import com.ctrip.framework.apollo.biz.service.ReleaseService;
15+
import com.ctrip.framework.apollo.common.constants.GsonType;
16+
import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
17+
import com.ctrip.framework.apollo.common.dto.ItemDTO;
18+
import com.ctrip.framework.apollo.common.exception.BadRequestException;
19+
import com.ctrip.framework.apollo.core.utils.StringUtils;
20+
21+
import org.aspectj.lang.annotation.After;
22+
import org.aspectj.lang.annotation.Aspect;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.stereotype.Component;
25+
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Objects;
29+
30+
31+
/**
32+
* unlock namespace if is redo operation.
33+
* --------------------------------------------
34+
* For example: If namespace has a item K1 = v1
35+
* --------------------------------------------
36+
* First operate: change k1 = v2 (lock namespace)
37+
* Second operate: change k1 = v1 (unlock namespace)
38+
*
39+
*/
40+
@Aspect
41+
@Component
42+
public class NamespaceUnlockAspect {
43+
44+
private Gson gson = new Gson();
45+
46+
@Autowired
47+
private NamespaceLockService namespaceLockService;
48+
@Autowired
49+
private NamespaceService namespaceService;
50+
@Autowired
51+
private ItemService itemService;
52+
@Autowired
53+
private ReleaseService releaseService;
54+
@Autowired
55+
private BizConfig bizConfig;
56+
57+
58+
//create item
59+
@After("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, item, ..)")
60+
public void requireLockAdvice(String appId, String clusterName, String namespaceName,
61+
ItemDTO item) {
62+
tryUnlock(namespaceService.findOne(appId, clusterName, namespaceName));
63+
}
64+
65+
//update item
66+
@After("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, itemId, item, ..)")
67+
public void requireLockAdvice(String appId, String clusterName, String namespaceName, long itemId,
68+
ItemDTO item) {
69+
tryUnlock(namespaceService.findOne(appId, clusterName, namespaceName));
70+
}
71+
72+
//update by change set
73+
@After("@annotation(PreAcquireNamespaceLock) && args(appId, clusterName, namespaceName, changeSet, ..)")
74+
public void requireLockAdvice(String appId, String clusterName, String namespaceName,
75+
ItemChangeSets changeSet) {
76+
tryUnlock(namespaceService.findOne(appId, clusterName, namespaceName));
77+
}
78+
79+
//delete item
80+
@After("@annotation(PreAcquireNamespaceLock) && args(itemId, operator, ..)")
81+
public void requireLockAdvice(long itemId, String operator) {
82+
Item item = itemService.findOne(itemId);
83+
if (item == null) {
84+
throw new BadRequestException("item not exist.");
85+
}
86+
tryUnlock(namespaceService.findOne(item.getNamespaceId()));
87+
}
88+
89+
private void tryUnlock(Namespace namespace) {
90+
if (bizConfig.isNamespaceLockSwitchOff()) {
91+
return;
92+
}
93+
94+
if (!isModified(namespace)) {
95+
namespaceLockService.unlock(namespace.getId());
96+
}
97+
98+
}
99+
100+
boolean isModified(Namespace namespace) {
101+
Release release = releaseService.findLatestActiveRelease(namespace);
102+
List<Item> items = itemService.findItems(namespace.getId());
103+
104+
if (release == null) {
105+
return hasNormalItems(items);
106+
}
107+
108+
Map<String, String> releasedConfiguration = gson.fromJson(release.getConfigurations(), GsonType.CONFIG);
109+
Map<String, String> configurationFromItems = Maps.newHashMap();
110+
111+
for (Item item : items) {
112+
String key = item.getKey();
113+
if (StringUtils.isBlank(key)) {
114+
continue;
115+
}
116+
//added
117+
if (releasedConfiguration.get(key) == null) {
118+
return true;
119+
}
120+
configurationFromItems.put(key, item.getValue());
121+
}
122+
123+
for (Map.Entry<String, String> entry : releasedConfiguration.entrySet()) {
124+
String key = entry.getKey();
125+
String value = entry.getValue();
126+
127+
//deleted or modified
128+
if (!Objects.equals(configurationFromItems.get(key), value)) {
129+
return true;
130+
}
131+
132+
}
133+
134+
return false;
135+
}
136+
137+
private boolean hasNormalItems(List<Item> items) {
138+
for (Item item : items) {
139+
if (!StringUtils.isEmpty(item.getKey())) {
140+
return true;
141+
}
142+
}
143+
144+
return false;
145+
}
146+
147+
}

apollo-adminservice/src/test/java/com/ctrip/framework/apollo/adminservice/AllTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ctrip.framework.apollo.adminservice;
22

33
import com.ctrip.framework.apollo.adminservice.aop.NamespaceLockTest;
4+
import com.ctrip.framework.apollo.adminservice.aop.NamespaceUnlockAspectTest;
45
import com.ctrip.framework.apollo.adminservice.controller.AppControllerTest;
56
import com.ctrip.framework.apollo.adminservice.controller.AppNamespaceControllerTest;
67
import com.ctrip.framework.apollo.adminservice.controller.ControllerExceptionTest;
@@ -17,7 +18,8 @@
1718
@SuiteClasses({
1819
AppControllerTest.class, ReleaseControllerTest.class, ItemSetControllerTest.class,
1920
ControllerExceptionTest.class, ControllerIntegrationExceptionTest.class,
20-
NamespaceLockTest.class, InstanceConfigControllerTest.class, AppNamespaceControllerTest.class
21+
NamespaceLockTest.class, InstanceConfigControllerTest.class, AppNamespaceControllerTest.class,
22+
NamespaceUnlockAspectTest.class
2123
})
2224
public class AllTests {
2325

apollo-adminservice/src/test/java/com/ctrip/framework/apollo/adminservice/aop/NamespaceLockTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class NamespaceLockTest {
4242
@Mock
4343
private BizConfig bizConfig;
4444
@InjectMocks
45-
NamespaceLockAspect namespaceLockAspect;
45+
NamespaceAcquireLockAspect namespaceLockAspect;
4646

4747
@Test
4848
public void acquireLockWithNotLockedAndSwitchON() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.ctrip.framework.apollo.adminservice.aop;
2+
3+
import com.ctrip.framework.apollo.biz.entity.Item;
4+
import com.ctrip.framework.apollo.biz.entity.Namespace;
5+
import com.ctrip.framework.apollo.biz.entity.Release;
6+
import com.ctrip.framework.apollo.biz.service.ItemService;
7+
import com.ctrip.framework.apollo.biz.service.ReleaseService;
8+
9+
import org.junit.Assert;
10+
import org.junit.Test;
11+
import org.junit.runner.RunWith;
12+
import org.mockito.InjectMocks;
13+
import org.mockito.Mock;
14+
import org.mockito.runners.MockitoJUnitRunner;
15+
16+
import java.util.Arrays;
17+
import java.util.Collections;
18+
import java.util.List;
19+
20+
import static org.mockito.Mockito.when;
21+
22+
@RunWith(MockitoJUnitRunner.class)
23+
public class NamespaceUnlockAspectTest {
24+
25+
@Mock
26+
private ReleaseService releaseService;
27+
@Mock
28+
private ItemService itemService;
29+
30+
@InjectMocks
31+
private NamespaceUnlockAspect namespaceUnlockAspect;
32+
33+
34+
@Test
35+
public void testNamespaceHasNoNormalItemsAndRelease() {
36+
37+
long namespaceId = 1;
38+
Namespace namespace = createNamespace(namespaceId);
39+
40+
when(releaseService.findLatestActiveRelease(namespace)).thenReturn(null);
41+
when(itemService.findItems(namespaceId)).thenReturn(Collections.singletonList(createItem("", "")));
42+
43+
boolean isModified = namespaceUnlockAspect.isModified(namespace);
44+
45+
Assert.assertFalse(isModified);
46+
}
47+
48+
@Test
49+
public void testNamespaceAddItem() {
50+
long namespaceId = 1;
51+
Namespace namespace = createNamespace(namespaceId);
52+
53+
Release release = createRelease("{\"k1\":\"v1\"}");
54+
List<Item> items = Arrays.asList(createItem("k1", "v1"), createItem("k2", "v2"));
55+
56+
when(releaseService.findLatestActiveRelease(namespace)).thenReturn(release);
57+
when(itemService.findItems(namespaceId)).thenReturn(items);
58+
59+
boolean isModified = namespaceUnlockAspect.isModified(namespace);
60+
61+
Assert.assertTrue(isModified);
62+
}
63+
64+
@Test
65+
public void testNamespaceModifyItem() {
66+
long namespaceId = 1;
67+
Namespace namespace = createNamespace(namespaceId);
68+
69+
Release release = createRelease("{\"k1\":\"v1\"}");
70+
List<Item> items = Arrays.asList(createItem("k1", "v2"));
71+
72+
when(releaseService.findLatestActiveRelease(namespace)).thenReturn(release);
73+
when(itemService.findItems(namespaceId)).thenReturn(items);
74+
75+
boolean isModified = namespaceUnlockAspect.isModified(namespace);
76+
77+
Assert.assertTrue(isModified);
78+
}
79+
80+
@Test
81+
public void testNamespaceDeleteItem() {
82+
long namespaceId = 1;
83+
Namespace namespace = createNamespace(namespaceId);
84+
85+
Release release = createRelease("{\"k1\":\"v1\"}");
86+
List<Item> items = Arrays.asList(createItem("k2", "v2"));
87+
88+
when(releaseService.findLatestActiveRelease(namespace)).thenReturn(release);
89+
when(itemService.findItems(namespaceId)).thenReturn(items);
90+
91+
boolean isModified = namespaceUnlockAspect.isModified(namespace);
92+
93+
Assert.assertTrue(isModified);
94+
}
95+
96+
private Namespace createNamespace(long namespaceId) {
97+
Namespace namespace = new Namespace();
98+
namespace.setId(namespaceId);
99+
return namespace;
100+
}
101+
102+
private Item createItem(String key, String value) {
103+
Item item = new Item();
104+
item.setKey(key);
105+
item.setValue(value);
106+
return item;
107+
}
108+
109+
private Release createRelease(String configuration) {
110+
Release release = new Release();
111+
release.setConfigurations(configuration);
112+
return release;
113+
}
114+
115+
}

0 commit comments

Comments
 (0)