diff --git a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ReleaseController.java b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ReleaseController.java index 1151b8697554ff2206e8994093d41deaf498c091..9aa581cef652ae762239746eef648891b22af5fc 100644 --- a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ReleaseController.java +++ b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/controller/ReleaseController.java @@ -65,8 +65,7 @@ public class ReleaseController { @PathVariable("namespaceName") String namespaceName) { Release release = configService.findRelease(appId, clusterName, namespaceName); if (release == null) { - throw new NotFoundException(String.format("latest release not found for %s %s %s", appId, - clusterName, namespaceName)); + return null; } else { return BeanUtils.transfrom(ReleaseDTO.class, release); } diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ClusterService.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ClusterService.java index ac70e2448db8ff5ef173f469fd549ada100db18c..0a532badba60d48c9b55730eb69fe81884663372 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ClusterService.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ClusterService.java @@ -46,10 +46,12 @@ public class ClusterService { } List clusters = clusterRepository.findByAppId(appId); - Collections.sort(clusters); if (clusters == null) { return Collections.emptyList(); } + + Collections.sort(clusters); + return clusters; } diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/aop/RepositoryAspect.java b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/aop/RepositoryAspect.java similarity index 92% rename from apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/aop/RepositoryAspect.java rename to apollo-common/src/main/java/com/ctrip/framework/apollo/common/aop/RepositoryAspect.java index a8fd263e336892e98f32c901ba5d59f93153d41d..64abea596043205991dc33276d2ff0563ceba615 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/aop/RepositoryAspect.java +++ b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/aop/RepositoryAspect.java @@ -1,4 +1,4 @@ -package com.ctrip.framework.apollo.biz.aop; +package com.ctrip.framework.apollo.common.aop; import com.dianping.cat.Cat; import com.dianping.cat.message.Message; @@ -10,9 +10,6 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; -/** - * @author Jason Song(song_s@ctrip.com) - */ @Aspect @Component public class RepositoryAspect { diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/MachineUtil.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/MachineUtil.java index 53b884da9a2975dbe696ad4614e2bc89f30f613e..4bc76482b28aef90329eee89db759f1204f810cb 100644 --- a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/MachineUtil.java +++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/MachineUtil.java @@ -31,21 +31,25 @@ public class MachineUtil { try { StringBuilder sb = new StringBuilder(); Enumeration e = NetworkInterface.getNetworkInterfaces(); - while (e.hasMoreElements()) { - NetworkInterface ni = e.nextElement(); - sb.append(ni.toString()); - byte[] mac = ni.getHardwareAddress(); - if (mac != null) { - ByteBuffer bb = ByteBuffer.wrap(mac); - try { - sb.append(bb.getChar()); - sb.append(bb.getChar()); - sb.append(bb.getChar()); - } catch (BufferUnderflowException shortHardwareAddressException) { //NOPMD - // mac with less than 6 bytes. continue + + if (e != null){ + while (e.hasMoreElements()) { + NetworkInterface ni = e.nextElement(); + sb.append(ni.toString()); + byte[] mac = ni.getHardwareAddress(); + if (mac != null) { + ByteBuffer bb = ByteBuffer.wrap(mac); + try { + sb.append(bb.getChar()); + sb.append(bb.getChar()); + sb.append(bb.getChar()); + } catch (BufferUnderflowException shortHardwareAddressException) { //NOPMD + // mac with less than 6 bytes. continue + } } } } + machinePiece = sb.toString().hashCode(); } catch (Throwable ex) { // exception sometimes happens with IBM JVM, use random diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalSettings.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalSettings.java index 75a1c5fddd7459616fa87f11b4457e9071829e03..1c05bcbf7d326d37df0be2174f24cc4ab091ff23 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalSettings.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalSettings.java @@ -1,7 +1,6 @@ package com.ctrip.framework.apollo.portal; -import com.google.common.base.Strings; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; @@ -10,7 +9,6 @@ import com.ctrip.framework.apollo.portal.service.ServerConfigService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.health.Health; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @@ -33,9 +31,8 @@ public class PortalSettings { private Logger logger = LoggerFactory.getLogger(PortalSettings.class); - private static final int HEALTH_CHECK_INTERVAL = 5000; + private static final int HEALTH_CHECK_INTERVAL = 10 * 1000; - private List allStrEnvs; @Autowired ApplicationContext applicationContext; @@ -43,9 +40,7 @@ public class PortalSettings { @Autowired private ServerConfigService serverConfigService; - private List allEnvs = new ArrayList(); - - private List activeEnvs; + private List allEnvs = new ArrayList<>(); //mark env up or down private Map envStatusMark = new ConcurrentHashMap<>(); @@ -54,19 +49,16 @@ public class PortalSettings { @PostConstruct private void postConstruct() { + //初始化portal支持操作的环境集合,线上的portal可能支持所有的环境操作,而线下环境则支持一部分. // 每个环境的portal支持哪些环境配置在数据库里 - String serverConfig = serverConfigService.getValue("apollo.portal.envs"); - if (!Strings.isNullOrEmpty(serverConfig)){ - String[] configedEnvs = serverConfig.split(","); - allStrEnvs = Arrays.asList(configedEnvs); - } - + String serverConfig = serverConfigService.getValue("apollo.portal.envs", "FAT,UAT,PRO"); + String[] configedEnvs = serverConfig.split(","); + List allStrEnvs = Arrays.asList(configedEnvs); for (String e : allStrEnvs) { allEnvs.add(Env.valueOf(e.toUpperCase())); } - for (Env env : allEnvs) { envStatusMark.put(env, true); } @@ -79,6 +71,10 @@ public class PortalSettings { } + public List getAllEnvs(){ + return allEnvs; + } + public List getActiveEnvs() { List activeEnvs = new LinkedList<>(); for (Env env : allEnvs) { @@ -86,15 +82,9 @@ public class PortalSettings { activeEnvs.add(env); } } - this.activeEnvs = activeEnvs; return activeEnvs; } - public Env getFirstAliveEnv() { - return activeEnvs.get(0); - } - - class HealthCheckTask implements Runnable { private static final int ENV_DIED_THREADHOLD = 2; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/API.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/API.java index 141786557cbac085884781351b384ed69473cb3a..a9252bcd498a71d09401e2d6a60d41421d0303da 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/API.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/API.java @@ -1,29 +1,11 @@ package com.ctrip.framework.apollo.portal.api; -import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.client.RestTemplate; - -import com.ctrip.framework.apollo.core.enums.Env; -import com.ctrip.framework.apollo.portal.service.ServiceLocator; public class API { @Autowired - protected ServiceLocator serviceLocator; - - @Autowired - private RestTemplateFactory restTemplateFactory; - - protected RestTemplate restTemplate; - - @PostConstruct - private void postConstruct() { - restTemplate = restTemplateFactory.getObject(); - } + protected RetryableRestTemplate restTemplate; - public String getAdminServiceHost(Env env) { - return serviceLocator.getServiceAddress(env).getHomepageUrl(); - } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java index 046443fb5b870043e5857c02111b943f2c3bec75..cf523059e3a25d3bd076ba37a2a21cfd10990c7a 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAPI.java @@ -16,7 +16,6 @@ import org.springframework.boot.actuate.health.Health; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -32,26 +31,19 @@ public class AdminServiceAPI { public static class HealthAPI extends API { public Health health(Env env) { - return restTemplate.getForObject(getAdminServiceHost(env) + "/health", Health.class); + return restTemplate.get(env, "/health", Health.class); } } @Service public static class AppAPI extends API { - public List findApps(Env env) { - AppDTO[] appDTOs = - restTemplate.getForObject("{host}/apps", AppDTO[].class, getAdminServiceHost(env)); - return Arrays.asList(appDTOs); - } - public AppDTO loadApp(Env env, String appId) { - return restTemplate.getForObject("{host}/apps/{appId}", AppDTO.class, getAdminServiceHost(env), appId); + return restTemplate.get(env, "apps/{appId}", AppDTO.class, appId); } public AppDTO createApp(Env env, AppDTO app) { - return restTemplate.postForEntity("{host}/apps", app, AppDTO.class, getAdminServiceHost(env)) - .getBody(); + return restTemplate.post(env, "apps", app, AppDTO.class); } } @@ -60,31 +52,30 @@ public class AdminServiceAPI { public static class NamespaceAPI extends API { public List findNamespaceByCluster(String appId, Env env, String clusterName) { - NamespaceDTO[] namespaceDTOs = restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces", - NamespaceDTO[].class, getAdminServiceHost(env), appId, - clusterName); + NamespaceDTO[] namespaceDTOs = restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces", + NamespaceDTO[].class, appId, + clusterName); return Arrays.asList(namespaceDTOs); } public NamespaceDTO loadNamespace(String appId, Env env, String clusterName, String namespaceName) { - NamespaceDTO - dto = - restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/" + namespaceName, - NamespaceDTO.class, getAdminServiceHost(env), appId, clusterName); + NamespaceDTO dto = + restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}", + NamespaceDTO.class, appId, clusterName, namespaceName); return dto; } public NamespaceDTO createNamespace(Env env, NamespaceDTO namespace) { return restTemplate - .postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces", namespace, NamespaceDTO.class, - getAdminServiceHost(env), namespace.getAppId(), namespace.getClusterName()).getBody(); + .post(env, "apps/{appId}/clusters/{clusterName}/namespaces", namespace, NamespaceDTO.class, + namespace.getAppId(), namespace.getClusterName()); } public AppNamespaceDTO createAppNamespace(Env env, AppNamespaceDTO appNamespace) { - return restTemplate.postForEntity("{host}/apps/{appId}/appnamespaces", appNamespace, AppNamespaceDTO.class, - getAdminServiceHost(env), appNamespace.getAppId()).getBody(); + return restTemplate + .post(env, "apps/{appId}/appnamespaces", appNamespace, AppNamespaceDTO.class, appNamespace.getAppId()); } } @@ -94,34 +85,32 @@ public class AdminServiceAPI { public List findItems(String appId, Env env, String clusterName, String namespaceName) { ItemDTO[] itemDTOs = - restTemplate - .getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items", - ItemDTO[].class, - getAdminServiceHost(env), appId, clusterName, namespaceName); + restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items", + ItemDTO[].class, appId, clusterName, namespaceName); return Arrays.asList(itemDTOs); } public void updateItemsByChangeSet(String appId, Env env, String clusterName, String namespace, ItemChangeSets changeSets) { - restTemplate.postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/itemset", - changeSets, Void.class, getAdminServiceHost(env), appId, clusterName, namespace); + restTemplate.post(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/itemset", + changeSets, Void.class, appId, clusterName, namespace); } public void updateItem(String appId, Env env, String clusterName, String namespace, long itemId, ItemDTO item) { - restTemplate.put("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{itemId}", - item, getAdminServiceHost(env), appId, clusterName, namespace, itemId); + restTemplate.put(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{itemId}", + item, appId, clusterName, namespace, itemId); } public ItemDTO createItem(String appId, Env env, String clusterName, String namespace, ItemDTO item) { - return restTemplate.postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items", - item, ItemDTO.class, getAdminServiceHost(env), appId, clusterName, namespace) - .getBody(); + return restTemplate.post(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items", + item, ItemDTO.class, appId, clusterName, namespace) + ; } public void deleteItem(Env env, long itemId, String operator) { - restTemplate.delete("{host}/items/{itemId}?operator={operator}", getAdminServiceHost(env), itemId, operator); + restTemplate.delete(env, "items/{itemId}?operator={operator}", itemId, operator); } } @@ -129,64 +118,64 @@ public class AdminServiceAPI { public static class ClusterAPI extends API { public List findClustersByApp(String appId, Env env) { - ClusterDTO[] clusterDTOs = restTemplate.getForObject("{host}/apps/{appId}/clusters", ClusterDTO[].class, - getAdminServiceHost(env), appId); + ClusterDTO[] clusterDTOs = restTemplate.get(env, "apps/{appId}/clusters", ClusterDTO[].class, + appId); return Arrays.asList(clusterDTOs); } public ClusterDTO loadCluster(String appId, Env env, String clusterName) { - return restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}", ClusterDTO.class, - getAdminServiceHost(env), appId, clusterName); + return restTemplate.get(env, "apps/{appId}/clusters/{clusterName}", ClusterDTO.class, + appId, clusterName); } public boolean isClusterUnique(String appId, Env env, String clusterName) { return restTemplate - .getForObject("{host}/apps/{appId}/cluster/{clusterName}/unique", Boolean.class, getAdminServiceHost(env), - appId, clusterName); + .get(env, "apps/{appId}/cluster/{clusterName}/unique", Boolean.class, + appId, clusterName); } public ClusterDTO create(Env env, ClusterDTO cluster) { - return restTemplate.postForObject("{host}/apps/{appId}/clusters", cluster, ClusterDTO.class, - getAdminServiceHost(env), cluster.getAppId()); + return restTemplate.post(env, "apps/{appId}/clusters", cluster, ClusterDTO.class, + cluster.getAppId()); } } @Service public static class ReleaseAPI extends API { - public List findReleases(String appId, Env env, String clusterName, String namespaceName, int page, int size){ - ReleaseDTO[] releaseDTOs = restTemplate.getForObject( - "{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases?page={page}&size={size}", + public List findReleases(String appId, Env env, String clusterName, String namespaceName, int page, + int size) { + ReleaseDTO[] releaseDTOs = restTemplate.get( + env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases?page={page}&size={size}", ReleaseDTO[].class, - getAdminServiceHost(env), appId, clusterName, namespaceName, page, size); + appId, clusterName, namespaceName, page, size); return Arrays.asList(releaseDTOs); } public ReleaseDTO loadLatestRelease(String appId, Env env, String clusterName, String namespace) { ReleaseDTO releaseDTO = restTemplate - .getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest", - ReleaseDTO.class, getAdminServiceHost(env), appId, clusterName, namespace); + .get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest", + ReleaseDTO.class, appId, clusterName, namespace); return releaseDTO; } public ReleaseDTO release(String appId, Env env, String clusterName, String namespace, - String releaseBy, String comment, String operator) { + String releaseTitle, String comment, String operator) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8")); MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.add("name", releaseBy); + parameters.add("name", releaseTitle); parameters.add("comment", comment); parameters.add("operator", operator); HttpEntity> entity = - new HttpEntity>(parameters, headers); - ResponseEntity response = - restTemplate - .postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", entity, - ReleaseDTO.class, - getAdminServiceHost(env), appId, clusterName, namespace); - return response.getBody(); + new HttpEntity<>(parameters, headers); + ReleaseDTO response = restTemplate.post( + env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", entity, + ReleaseDTO.class, + appId, clusterName, namespace); + return response; } } @@ -195,10 +184,10 @@ public class AdminServiceAPI { public List find(String appId, Env env, String clusterName, String namespaceName, int page, int size) { - CommitDTO[] commitDTOs = restTemplate.getForObject( - "{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/commit?page={page}&size={size}", - CommitDTO[].class, - getAdminServiceHost(env), appId, clusterName, namespaceName, page, size); + CommitDTO[] commitDTOs = restTemplate.get(env, + "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/commit?page={page}&size={size}", + CommitDTO[].class, + appId, clusterName, namespaceName, page, size); return Arrays.asList(commitDTOs); } @@ -208,9 +197,9 @@ public class AdminServiceAPI { public static class NamespaceLockAPI extends API { public NamespaceLockDTO getNamespaceLockOwner(String appId, Env env, String clusterName, String namespaceName) { - return restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/lock", - NamespaceLockDTO.class, - getAdminServiceHost(env), appId, clusterName, namespaceName); + return restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/lock", + NamespaceLockDTO.class, + appId, clusterName, namespaceName); } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAddressLocator.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAddressLocator.java new file mode 100644 index 0000000000000000000000000000000000000000..07d5c63d5adf69670bae879f460fbb7963ad637e --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/AdminServiceAddressLocator.java @@ -0,0 +1,109 @@ +package com.ctrip.framework.apollo.portal.api; + +import com.ctrip.framework.apollo.core.MetaDomainConsts; +import com.ctrip.framework.apollo.core.dto.ServiceDTO; +import com.ctrip.framework.apollo.core.enums.Env; +import com.ctrip.framework.apollo.portal.PortalSettings; +import com.dianping.cat.Cat; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.web.HttpMessageConverters; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.annotation.PostConstruct; + +@Component +public class AdminServiceAddressLocator { + + private static final int DEFAULT_TIMEOUT_MS = 1000; + private static final long REFRESH_INTERVAL = 5 * 60 * 1000; + private static final int RETRY_TIMES = 3; + private static final String ADMIN_SERVICE_URL_PATH = "/services/admin"; + + private ScheduledExecutorService refreshServiceAddressService; + private RestTemplate restTemplate; + private List allEnvs; + private Map> cache = new ConcurrentHashMap<>(); + + @Autowired + private HttpMessageConverters httpMessageConverters; + @Autowired + private PortalSettings portalSettings; + + @PostConstruct + public void init() { + allEnvs = portalSettings.getAllEnvs(); + + //init restTemplate + restTemplate = new RestTemplate(httpMessageConverters.getConverters()); + if (restTemplate.getRequestFactory() instanceof SimpleClientHttpRequestFactory) { + SimpleClientHttpRequestFactory rf = + (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory(); + rf.setReadTimeout(DEFAULT_TIMEOUT_MS); + rf.setConnectTimeout(DEFAULT_TIMEOUT_MS); + } else if (restTemplate.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory) { + HttpComponentsClientHttpRequestFactory rf = + (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory(); + rf.setReadTimeout(DEFAULT_TIMEOUT_MS); + rf.setConnectTimeout(DEFAULT_TIMEOUT_MS); + } + + refreshServiceAddressService = Executors.newScheduledThreadPool(1); + + refreshServiceAddressService.scheduleWithFixedDelay( + new RefreshAdminServerAddressTask(), 0, REFRESH_INTERVAL, + TimeUnit.MILLISECONDS); + } + + public List getServiceList(Env env) { + return cache.get(env); + } + + //Maintain admin server address + class RefreshAdminServerAddressTask implements Runnable { + + @Override + public void run() { + for (Env env : allEnvs) { + refreshServerAddressCache(env); + } + } + } + + private void refreshServerAddressCache(Env env) { + + for (int i = 0; i < RETRY_TIMES; i++) { + + try { + ServiceDTO[] services = getAdminServerAddress(env); + if (services == null || services.length == 0) { + continue; + } + cache.put(env, Arrays.asList(services)); + break; + } catch (Throwable e) {//meta server error + Cat.logError("get admin server address fail", e); + continue; + } + } + } + + private ServiceDTO[] getAdminServerAddress(Env env) { + String domainName = MetaDomainConsts.getDomain(env); + String url = domainName + ADMIN_SERVICE_URL_PATH; + return restTemplate.getForObject(url, ServiceDTO[].class); + } + + +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/RetryableRestTemplate.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/RetryableRestTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..e9895d7684503b327a5bfd4b366ac007d4040e9e --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/api/RetryableRestTemplate.java @@ -0,0 +1,150 @@ +package com.ctrip.framework.apollo.portal.api; + +import com.ctrip.framework.apollo.core.dto.ServiceDTO; +import com.ctrip.framework.apollo.core.enums.Env; +import com.ctrip.framework.apollo.core.exception.ServiceException; +import com.ctrip.framework.apollo.portal.constant.CatEventType; +import com.dianping.cat.Cat; +import com.dianping.cat.message.Message; +import com.dianping.cat.message.Transaction; + +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.HttpHostConnectException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriTemplateHandler; +import org.springframework.web.util.UriTemplateHandler; + +import java.net.SocketTimeoutException; +import java.util.List; + +import javax.annotation.PostConstruct; + +/** + * 封装RestTemplate. admin server集群在某些机器宕机或者超时的情况下轮询重试 + */ +@Component +public class RetryableRestTemplate { + + private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler(); + + private RestTemplate restTemplate; + + @Autowired + private RestTemplateFactory restTemplateFactory; + @Autowired + private AdminServiceAddressLocator adminServiceAddressLocator; + + + @PostConstruct + private void postConstruct() { + restTemplate = restTemplateFactory.getObject(); + } + + public T get(Env env, String path, Class responseType, Object... urlVariables) + throws RestClientException { + return execute(HttpMethod.GET, env, path, null, responseType, urlVariables); + } + + public T post(Env env, String path, Object request, Class responseType, Object... uriVariables) + throws RestClientException { + return execute(HttpMethod.POST, env, path, request, responseType, uriVariables); + } + + public void put(Env env, String path, Object request, Object... urlVariables) throws RestClientException { + execute(HttpMethod.PUT, env, path, request, null, urlVariables); + } + + public void delete(Env env, String path, Object... urlVariables) throws RestClientException { + execute(HttpMethod.DELETE, env, path, null, null, urlVariables); + } + + private T execute(HttpMethod method, Env env, String path, Object request, Class responseType, + Object... uriVariables) { + + String uri = uriTemplateHandler.expand(path, uriVariables).getPath(); + Transaction ct = Cat.newTransaction("AdminAPI", uri); + + List services = adminServiceAddressLocator.getServiceList(env); + + if (CollectionUtils.isEmpty(services)) { + ServiceException e = new ServiceException("No available admin service"); + ct.setStatus(e); + ct.complete(); + throw e; + } + + for (ServiceDTO serviceDTO : services) { + try { + + T result = doExecute(method, serviceDTO, path, request, responseType, uriVariables); + + ct.setStatus(Message.SUCCESS); + ct.complete(); + return result; + } catch (Throwable t) { + Cat.logError(t); + if (canRetry(t, method)) { + Cat.logEvent(CatEventType.API_RETRY, uri); + continue; + } else {//biz exception rethrow + ct.setStatus(t); + ct.complete(); + throw t; + } + } + } + + //all admin server down + ServiceException e = new ServiceException("No available admin service"); + ct.setStatus(e); + ct.complete(); + throw e; + } + + private T doExecute(HttpMethod method, ServiceDTO service, String path, Object request, + Class responseType, + Object... uriVariables) { + T result = null; + switch (method) { + case GET: + result = restTemplate.getForObject(parseHost(service) + path, responseType, uriVariables); + break; + case POST: + result = + restTemplate.postForEntity(parseHost(service) + path, request, responseType, uriVariables).getBody(); + break; + case PUT: + restTemplate.put(parseHost(service) + path, request, uriVariables); + break; + case DELETE: + restTemplate.delete(parseHost(service) + path, uriVariables); + break; + default: + throw new UnsupportedOperationException(String.format("not supported http method(method=%s)", method)); + } + return result; + } + + private String parseHost(ServiceDTO serviceAddress) { + return serviceAddress.getHomepageUrl() + "/"; + } + + //post,delete,put请求在admin server处理超时情况下不重试 + private boolean canRetry(Throwable e, HttpMethod method) { + Throwable nestedException = e.getCause(); + if (method == HttpMethod.GET) { + return nestedException instanceof SocketTimeoutException + || nestedException instanceof HttpHostConnectException + || nestedException instanceof ConnectTimeoutException; + } else { + return nestedException instanceof HttpHostConnectException + || nestedException instanceof ConnectTimeoutException; + } + } + +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripLogoutHandler.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripLogoutHandler.java similarity index 87% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripLogoutHandler.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripLogoutHandler.java index 9e69a67bf5c7efc505b55c68c4028b50b7665a10..8e90f8d6d603bde948a85412ecf6459f3610d6b1 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripLogoutHandler.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripLogoutHandler.java @@ -1,5 +1,6 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.ctrip; +import com.ctrip.framework.apollo.portal.auth.LogoutHandler; import com.ctrip.framework.apollo.portal.service.ServerConfigService; import org.springframework.beans.factory.annotation.Autowired; @@ -11,7 +12,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -public class CtripLogoutHandler implements LogoutHandler{ +public class CtripLogoutHandler implements LogoutHandler { @Autowired private ServerConfigService serverConfigService; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripSsoHeartbeatHandler.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripSsoHeartbeatHandler.java similarity index 78% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripSsoHeartbeatHandler.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripSsoHeartbeatHandler.java index 66cbabd644dae4d1d2e638eb36d79c9cbf4842f1..a4e9c65da8e621511b8a8c0f7fc03682f5b68282 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripSsoHeartbeatHandler.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripSsoHeartbeatHandler.java @@ -1,4 +1,6 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.ctrip; + +import com.ctrip.framework.apollo.portal.auth.SsoHeartbeatHandler; import java.io.IOException; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripUserInfoHolder.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserInfoHolder.java similarity index 86% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripUserInfoHolder.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserInfoHolder.java index e2c79c44211fa1a7b7c79c176cc26e1407982f34..fe1170e71d02ca54af22eec28ee803c245ea2131 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripUserInfoHolder.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserInfoHolder.java @@ -1,5 +1,6 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.ctrip; +import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; import com.ctrip.framework.apollo.portal.entity.po.UserInfo; import java.lang.reflect.Method; @@ -7,7 +8,7 @@ import java.lang.reflect.Method; /** * ctrip内部实现的获取用户信息 */ -public class CtripUserInfoHolder implements UserInfoHolder{ +public class CtripUserInfoHolder implements UserInfoHolder { private Object assertionHolder; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripUserService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserService.java similarity index 98% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripUserService.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserService.java index 874c7934de41db4c542af8c63ed3eb373fc28d61..481c682c210d163114c3ec14786798bbc7764851 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/CtripUserService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserService.java @@ -1,4 +1,4 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.ctrip; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; @@ -62,7 +62,7 @@ public class CtripUserService implements UserService { } @Override - public List searchUsers(String keyword, int offset, int limit) { + public List searchUsers(String keyword, int offset, int limit) { UserServiceRequest request = assembleSearchUserRequest(keyword, offset, limit); HttpEntity entity = new HttpEntity<>(request); diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultLogoutHandler.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultLogoutHandler.java similarity index 65% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultLogoutHandler.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultLogoutHandler.java index 50b30f71b9d8eda85aff57be244d622caa363d27..df49720c872ff61ed20802ed8a881bd79d3c1b68 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultLogoutHandler.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultLogoutHandler.java @@ -1,11 +1,13 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.defaultimpl; + +import com.ctrip.framework.apollo.portal.auth.LogoutHandler; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public class DefaultLogoutHandler implements LogoutHandler{ +public class DefaultLogoutHandler implements LogoutHandler { @Override public void logout(HttpServletRequest request, HttpServletResponse response) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultSsoHeartbeatHandler.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultSsoHeartbeatHandler.java similarity index 78% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultSsoHeartbeatHandler.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultSsoHeartbeatHandler.java index 3822d42ef7c28307d1b78fb8ca9cfc62a8c60539..affbcb474d64d2b6853bc0667d24e36c49f40f4b 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultSsoHeartbeatHandler.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultSsoHeartbeatHandler.java @@ -1,4 +1,6 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.defaultimpl; + +import com.ctrip.framework.apollo.portal.auth.SsoHeartbeatHandler; import java.io.IOException; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultUserInfoHolder.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultUserInfoHolder.java similarity index 62% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultUserInfoHolder.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultUserInfoHolder.java index 0320282cec8b3e18281e2d420e862129b5e628cc..961a13fea32f0954671433248152db7ea225aa48 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultUserInfoHolder.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultUserInfoHolder.java @@ -1,11 +1,12 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.defaultimpl; +import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; import com.ctrip.framework.apollo.portal.entity.po.UserInfo; /** * 不是ctrip的公司默认提供一个假用户 */ -public class DefaultUserInfoHolder implements UserInfoHolder{ +public class DefaultUserInfoHolder implements UserInfoHolder { public DefaultUserInfoHolder(){ diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultUserService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultUserService.java similarity index 94% rename from apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultUserService.java rename to apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultUserService.java index 6cbae7c5bd6fefb6192b337280bfd1d0855e768b..0c19e33b5c2d49ddaed24325ac4745db4913f529 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/DefaultUserService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/auth/defaultimpl/DefaultUserService.java @@ -1,4 +1,4 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.defaultimpl; import com.google.common.collect.Lists; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configutation/AuthConfiguration.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configutation/AuthConfiguration.java index bc406fbec65faef65adccec2308c48b4d86a7c4c..c7a7ef484cb9365ec8710dc7a4666f2d43d6d5a2 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configutation/AuthConfiguration.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/configutation/AuthConfiguration.java @@ -1,13 +1,13 @@ package com.ctrip.framework.apollo.portal.configutation; -import com.ctrip.framework.apollo.portal.auth.CtripLogoutHandler; -import com.ctrip.framework.apollo.portal.auth.CtripSsoHeartbeatHandler; -import com.ctrip.framework.apollo.portal.auth.CtripUserInfoHolder; -import com.ctrip.framework.apollo.portal.auth.CtripUserService; -import com.ctrip.framework.apollo.portal.auth.DefaultLogoutHandler; -import com.ctrip.framework.apollo.portal.auth.DefaultSsoHeartbeatHandler; -import com.ctrip.framework.apollo.portal.auth.DefaultUserInfoHolder; -import com.ctrip.framework.apollo.portal.auth.DefaultUserService; +import com.ctrip.framework.apollo.portal.auth.ctrip.CtripLogoutHandler; +import com.ctrip.framework.apollo.portal.auth.ctrip.CtripSsoHeartbeatHandler; +import com.ctrip.framework.apollo.portal.auth.ctrip.CtripUserInfoHolder; +import com.ctrip.framework.apollo.portal.auth.ctrip.CtripUserService; +import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultLogoutHandler; +import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultSsoHeartbeatHandler; +import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultUserInfoHolder; +import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultUserService; import com.ctrip.framework.apollo.portal.auth.LogoutHandler; import com.ctrip.framework.apollo.portal.auth.SsoHeartbeatHandler; import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/constant/CatEventType.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/constant/CatEventType.java new file mode 100644 index 0000000000000000000000000000000000000000..fd77f4e446fd2e7b1f8c478358532d49ed0f9a4a --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/constant/CatEventType.java @@ -0,0 +1,21 @@ +package com.ctrip.framework.apollo.portal.constant; + +public interface CatEventType { + + String RELEASE_NAMESPACE = "Namespace.Release"; + + String MODIFY_NAMESPACE_BY_TEXT = "Namespace.Modify.Text"; + + String MODIFY_NAMESPACE = "Namespace.Modify"; + + String SYNC_NAMESPACE = "Namespace.Sync"; + + String CREATE_APP = "App.Create"; + + String CREATE_CLUSTER = "Cluster.Create"; + + String CREATE_NAMESPACE = "Namespace.Create"; + + String API_RETRY = "API.Retry"; + +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/form/NamespaceReleaseModel.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/form/NamespaceReleaseModel.java index 94da939a8ce3336089418283558c080ad8cec29d..52fe42f019821f5dfd4c461b905fcfce71760bd7 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/form/NamespaceReleaseModel.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/form/NamespaceReleaseModel.java @@ -10,12 +10,12 @@ public class NamespaceReleaseModel implements Verifiable { private String env; private String clusterName; private String namespaceName; - private String releaseBy; + private String releaseTitle; private String releaseComment; @Override public boolean isInvalid() { - return StringUtils.isContainEmpty(appId, env, clusterName, namespaceName, releaseBy); + return StringUtils.isContainEmpty(appId, env, clusterName, namespaceName, releaseTitle); } public String getAppId() { @@ -50,12 +50,12 @@ public class NamespaceReleaseModel implements Verifiable { this.namespaceName = namespaceName; } - public String getReleaseBy() { - return releaseBy; + public String getReleaseTitle() { + return releaseTitle; } - public void setReleaseBy(String releaseBy) { - this.releaseBy = releaseBy; + public void setReleaseTitle(String releaseTitle) { + this.releaseTitle = releaseTitle; } public String getReleaseComment() { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/listener/CreationListener.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/listener/CreationListener.java index 7c1bbb14a468732ca95dfbbce5c03423273543ef..9f98387227c8c955dde3bdbb3608b3dcda061d98 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/listener/CreationListener.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/listener/CreationListener.java @@ -6,7 +6,7 @@ import com.ctrip.framework.apollo.core.dto.AppNamespaceDTO; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.portal.PortalSettings; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; -import com.ctrip.framework.apollo.portal.service.RoleInitializationService; +import com.dianping.cat.Cat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,8 +27,6 @@ public class CreationListener { private AdminServiceAPI.AppAPI appAPI; @Autowired private AdminServiceAPI.NamespaceAPI namespaceAPI; - @Autowired - private RoleInitializationService roleInitializationService; @EventListener public void onAppCreationEvent(AppCreationEvent event) { @@ -38,20 +36,22 @@ public class CreationListener { try { appAPI.createApp(env, appDTO); } catch (Throwable e) { - logger.error("call appAPI.createApp error.[{app}, {env}]", appDTO.getAppId(), env, e); + logger.error("call appAPI.createApp error.(appId={appId}, env={env})", appDTO.getAppId(), env, e); + Cat.logError(String.format("call appAPI.createApp error. (appId=%s, env=%s)", appDTO.getAppId(), env), e); } } } @EventListener public void onAppNamespaceCreationEvent(AppNamespaceCreationEvent event){ - AppNamespaceDTO dto = BeanUtils.transfrom(AppNamespaceDTO.class, event.getAppNamespace()); + AppNamespaceDTO appNamespace = BeanUtils.transfrom(AppNamespaceDTO.class, event.getAppNamespace()); List envs = portalSettings.getActiveEnvs(); for (Env env : envs) { try { - namespaceAPI.createAppNamespace(env, dto); + namespaceAPI.createAppNamespace(env, appNamespace); } catch (Throwable e) { - logger.error("call namespaceAPI.createAppNamespace error. [{app}, {env}]", dto.getAppId(), env, e); + logger.error("call appAPI.createApp error.(appId={appId}, env={env})", appNamespace.getAppId(), env, e); + Cat.logError(String.format("call appAPI.createApp error. (appId=%s, env=%s)", appNamespace.getAppId(), env), e); } } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java index 479979e1f249c300abb53be779233d6eebc66b61..bbc8eacacfbfc79eff52793291e36ef71674e23f 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java @@ -20,8 +20,10 @@ import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.exception.BadRequestException; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; +import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.entity.vo.EnvClusterInfo; import com.ctrip.framework.apollo.portal.repository.AppRepository; +import com.dianping.cat.Cat; @Service public class AppService { @@ -94,6 +96,8 @@ public class AppService { appNamespaceService.createDefaultAppNamespace(appId); //role roleInitializationService.initAppRoles(createdApp); + + Cat.logEvent(CatEventType.CREATE_APP, appId); return createdApp; } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ClusterService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ClusterService.java index 8963d6c23090aa905c5bf738333b8c14c58feef5..f029ce4d7e73239ecdb6890dc0472e39782336a0 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ClusterService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ClusterService.java @@ -4,6 +4,8 @@ import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.dto.ClusterDTO; import com.ctrip.framework.apollo.core.exception.BadRequestException; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; +import com.ctrip.framework.apollo.portal.constant.CatEventType; +import com.dianping.cat.Cat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -24,7 +26,11 @@ public class ClusterService { if (!clusterAPI.isClusterUnique(cluster.getAppId(), env, cluster.getName())){ throw new BadRequestException(String.format("cluster %s already exists.", cluster.getName())); } - return clusterAPI.create(env, cluster); + ClusterDTO clusterDTO = clusterAPI.create(env, cluster); + + Cat.logEvent(CatEventType.CREATE_CLUSTER, cluster.getAppId(), "0", cluster.getName()); + + return clusterDTO; } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigService.java index a777b15ffa85e0afb1efe2f469380a0f4f07d60e..b7eb36b93888504d2130d310b0038e46b6e4afb8 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigService.java @@ -1,8 +1,6 @@ package com.ctrip.framework.apollo.portal.service; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; @@ -11,20 +9,23 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.client.HttpClientErrorException; import com.ctrip.framework.apollo.common.utils.BeanUtils; +import com.ctrip.framework.apollo.core.dto.ReleaseDTO; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.dto.ItemChangeSets; import com.ctrip.framework.apollo.core.dto.ItemDTO; import com.ctrip.framework.apollo.core.dto.NamespaceDTO; import com.ctrip.framework.apollo.core.exception.BadRequestException; -import com.ctrip.framework.apollo.core.exception.ServiceException; import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; +import com.ctrip.framework.apollo.portal.constant.CatEventType; +import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.vo.ItemDiffs; import com.ctrip.framework.apollo.portal.entity.vo.NamespaceIdentifer; import com.ctrip.framework.apollo.portal.entity.form.NamespaceTextModel; import com.ctrip.framework.apollo.portal.service.txtresolver.ConfigTextResolver; +import com.dianping.cat.Cat; import java.util.LinkedList; import java.util.List; @@ -33,8 +34,6 @@ import java.util.Map; @Service public class ConfigService { - private Logger logger = LoggerFactory.getLogger(ConfigService.class); - @Autowired private UserInfoHolder userInfoHolder; @Autowired @@ -77,6 +76,9 @@ public class ConfigService { changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId()); itemAPI.updateItemsByChangeSet(appId, env, clusterName, namespaceName, changeSets); + + Cat.logEvent(CatEventType.MODIFY_NAMESPACE_BY_TEXT, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); + Cat.logEvent(CatEventType.MODIFY_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); } @@ -92,7 +94,9 @@ public class ConfigService { item.setDataChangeCreatedBy(username); item.setDataChangeLastModifiedBy(username); - return itemAPI.createItem(appId, env, clusterName, namespaceName, item); + ItemDTO itemDTO = itemAPI.createItem(appId, env, clusterName, namespaceName, item); + Cat.logEvent(CatEventType.MODIFY_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); + return itemDTO; } public void updateItem(String appId, Env env, String clusterName, String namespaceName, ItemDTO item) { @@ -115,17 +119,16 @@ public class ConfigService { NamespaceIdentifer namespaceIdentifer = itemDiff.getNamespace(); ItemChangeSets changeSets = itemDiff.getDiffs(); changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId()); - try { - itemAPI - .updateItemsByChangeSet(namespaceIdentifer.getAppId(), namespaceIdentifer.getEnv(), - namespaceIdentifer.getClusterName(), - namespaceIdentifer.getNamespaceName(), changeSets); - } catch (HttpClientErrorException e) { - logger.error("sync items error. namespace:{}", namespaceIdentifer); - throw new ServiceException(String.format("sync item error. env:%s, clusterName:%s", namespaceIdentifer.getEnv(), - namespaceIdentifer.getClusterName()), e); + + String appId = namespaceIdentifer.getAppId(); + Env env = namespaceIdentifer.getEnv(); + String clusterName = namespaceIdentifer.getClusterName(); + String namespaceName = namespaceIdentifer.getNamespaceName(); + + itemAPI.updateItemsByChangeSet(appId, env, clusterName, namespaceName, changeSets); + + Cat.logEvent(CatEventType.SYNC_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); } - } } public List compare(List comparedNamespaces, List sourceItems) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java index 88b3f34b692e7d02f713ad66f3e9d334dfa1a804..617bb9ca9f07cd1576d675af5be4996c133786f0 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java @@ -4,7 +4,6 @@ import com.google.gson.Gson; import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.utils.BeanUtils; -import com.ctrip.framework.apollo.common.utils.ExceptionUtils; import com.ctrip.framework.apollo.core.dto.ItemDTO; import com.ctrip.framework.apollo.core.dto.NamespaceDTO; import com.ctrip.framework.apollo.core.dto.ReleaseDTO; @@ -13,14 +12,14 @@ import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; +import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; +import com.dianping.cat.Cat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; -import org.springframework.web.client.HttpClientErrorException; import java.util.Collections; import java.util.HashMap; @@ -42,13 +41,10 @@ public class NamespaceService { private AdminServiceAPI.ReleaseAPI releaseAPI; @Autowired private AdminServiceAPI.NamespaceAPI namespaceAPI; - - @Autowired private AppNamespaceService appNamespaceService; - public NamespaceDTO createNamespace(Env env, NamespaceDTO namespace) { if (StringUtils.isEmpty(namespace.getDataChangeCreatedBy())) { namespace.setDataChangeCreatedBy(userInfoHolder.getUser().getUserId()); @@ -56,6 +52,9 @@ public class NamespaceService { namespace.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId()); NamespaceDTO createdNamespace = namespaceAPI.createNamespace(env, namespace); + Cat.logEvent(CatEventType.CREATE_NAMESPACE, + String.format("%s+%s+%s+%s", namespace.getAppId(), env, namespace.getClusterName(), + namespace.getNamespaceName())); return createdNamespace; } @@ -100,17 +99,11 @@ public class NamespaceService { String namespaceName = namespace.getNamespaceName(); //latest Release - ReleaseDTO release = null; + ReleaseDTO latestRelease = null; Map releaseItems = new HashMap<>(); - try { - release = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName); - releaseItems = gson.fromJson(release.getConfigurations(), Map.class); - } catch (HttpClientErrorException e) { - if (e.getStatusCode() == HttpStatus.NOT_FOUND) { - logger.warn(ExceptionUtils.toString(e)); - } else { - throw e; - } + latestRelease = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName); + if (latestRelease != null){ + releaseItems = gson.fromJson(latestRelease.getConfigurations(), Map.class); } //not Release config items @@ -161,6 +154,7 @@ public class NamespaceService { namespace.setPublic(isPublic); } + private List parseDeletedItems(List newItems, Map releaseItems) { Map newItemMap = BeanUtils.mapByKey("key", newItems); diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java index dda059b6c1cc187c58f6c7f54c7edbf2de4586ec..1ca5227d9cd437abfab268481690b944be151962 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java @@ -6,8 +6,10 @@ import com.ctrip.framework.apollo.core.dto.ReleaseDTO; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; +import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO; +import com.dianping.cat.Cat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -22,6 +24,7 @@ import java.util.Set; @Service public class ReleaseService { + private static final Gson gson = new Gson(); @Autowired @@ -30,26 +33,33 @@ public class ReleaseService { private AdminServiceAPI.ReleaseAPI releaseAPI; public ReleaseDTO createRelease(NamespaceReleaseModel model) { - return releaseAPI.release(model.getAppId(), model.getEnv(), model.getClusterName(), - model.getNamespaceName(), model.getReleaseBy(), model.getReleaseComment() - , userInfoHolder.getUser().getUserId()); + String appId = model.getAppId(); + Env env = model.getEnv(); + String clusterName = model.getClusterName(); + String namespaceName = model.getNamespaceName(); + ReleaseDTO releaseDTO = + releaseAPI.release(appId, env, clusterName, namespaceName, model.getReleaseTitle(), model.getReleaseComment() + , userInfoHolder.getUser().getUserId()); + Cat.logEvent(CatEventType.RELEASE_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); + return releaseDTO; } - public List findReleases(String appId, Env env, String clusterName, String namespaceName, int page, int size){ + public List findReleases(String appId, Env env, String clusterName, String namespaceName, int page, + int size) { List releaseDTOs = releaseAPI.findReleases(appId, env, clusterName, namespaceName, page, size); - if (CollectionUtils.isEmpty(releaseDTOs)){ + if (CollectionUtils.isEmpty(releaseDTOs)) { return Collections.EMPTY_LIST; } List releases = new LinkedList<>(); - for (ReleaseDTO releaseDTO: releaseDTOs){ + for (ReleaseDTO releaseDTO : releaseDTOs) { ReleaseVO release = new ReleaseVO(); release.setBaseInfo(releaseDTO); Set kvEntities = new LinkedHashSet<>(); Set entries = gson.fromJson(releaseDTO.getConfigurations(), Map.class).entrySet(); - for(Map.Entry entry: entries){ + for (Map.Entry entry : entries) { kvEntities.add(new ReleaseVO.KVEntity(entry.getKey(), entry.getValue())); } release.setItems(kvEntities); diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServiceLocator.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServiceLocator.java deleted file mode 100644 index e7991ad6ced4f1e41a9aa9453695f92750797ec6..0000000000000000000000000000000000000000 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServiceLocator.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.ctrip.framework.apollo.portal.service; - -import java.net.URI; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.PostConstruct; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.web.HttpMessageConverters; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import com.ctrip.framework.apollo.core.MetaDomainConsts; -import com.ctrip.framework.apollo.core.dto.ServiceDTO; -import com.ctrip.framework.apollo.core.enums.Env; -import com.ctrip.framework.apollo.core.exception.ServiceException; - -/** - * @author liuym - */ -@Service -public class ServiceLocator { - - private static final Logger logger = LoggerFactory.getLogger(ServiceLocator.class); - - private static final int DEFAULT_TIMEOUT_MS = 1000; - - private static final int RETRY_TIMES = 3; - - private static final int CALL_META_SERVER_THRESHOLD = 2; - - private static final String ADMIN_SERVICE_URL_PATH = "/services/admin"; - - private RestTemplate restTemplate; - - @Autowired - private HttpMessageConverters httpMessageConverters; - - private Map serviceAddressCache = new ConcurrentHashMap<>(); - - private final AtomicInteger adminCallCounts = new AtomicInteger(0); - - @PostConstruct - private void postConstruct() { - restTemplate = new RestTemplate(httpMessageConverters.getConverters()); - if (restTemplate.getRequestFactory() instanceof SimpleClientHttpRequestFactory) { - SimpleClientHttpRequestFactory rf = - (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory(); - rf.setReadTimeout(DEFAULT_TIMEOUT_MS); - rf.setConnectTimeout(DEFAULT_TIMEOUT_MS); - } else if (restTemplate.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory) { - HttpComponentsClientHttpRequestFactory rf = - (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory(); - rf.setReadTimeout(DEFAULT_TIMEOUT_MS); - rf.setConnectTimeout(DEFAULT_TIMEOUT_MS); - } - } - - - public ServiceDTO getServiceAddress(Env env) throws ServiceException { - - if (adminCallCounts.get() % CALL_META_SERVER_THRESHOLD == 0) { - return getServiceAddressFromMetaServer(env); - } else { - //if cached then return from cache - ServiceDTO[] serviceDTOs = serviceAddressCache.get(env); - if (serviceDTOs != null && serviceDTOs.length > 0){ - return randomServiceAddress(serviceDTOs); - }else {//return from meta server - return getServiceAddressFromMetaServer(env); - } - } - - - } - - public ServiceDTO getServiceAddressFromMetaServer(Env env) { - //retry - for (int i = 0; i < RETRY_TIMES; i++) { - ServiceDTO[] services = getServices(env); - if (services != null && services.length > 0) { - serviceAddressCache.put(env, services); - return randomServiceAddress(services); - } else { - logger.warn(String.format("can not get %s admin service address at %d time", env, i)); - } - } - //clear cache - serviceAddressCache.remove(env); - logger.error(String.format("can not get %s admin service address", env)); - throw new ServiceException("No available admin service"); - } - - - private ServiceDTO[] getServices(Env env) { - String domainName = MetaDomainConsts.getDomain(env); - String url = domainName + ADMIN_SERVICE_URL_PATH; - - try { - return restTemplate.getForObject(new URI(url), ServiceDTO[].class); - } catch (Exception ex) { - logger.warn(ex.getMessage()); - return null; - } - } - - private ServiceDTO randomServiceAddress(ServiceDTO[] services){ - return services[Math.abs(adminCallCounts.getAndIncrement()) % services.length]; - } - -} diff --git a/apollo-portal/src/main/resources/static/app.html b/apollo-portal/src/main/resources/static/app.html index 7024f2949a570af0aa535108f76654d38d14fdb5..148233695f430c87dccfcc9bb98f3c58c1a104a4 100644 --- a/apollo-portal/src/main/resources/static/app.html +++ b/apollo-portal/src/main/resources/static/app.html @@ -57,7 +57,7 @@
- +
diff --git a/apollo-portal/src/main/resources/static/app/role.html b/apollo-portal/src/main/resources/static/app/role.html index 26006a91c748b432efd9b530583e826df583b80d..aaa510d5e32f6ec5bd5dccbf77d52f5d04e96020 100644 --- a/apollo-portal/src/main/resources/static/app/role.html +++ b/apollo-portal/src/main/resources/static/app/role.html @@ -40,7 +40,7 @@
- +
diff --git a/apollo-portal/src/main/resources/static/cluster.html b/apollo-portal/src/main/resources/static/cluster.html index 2456b4c5642326c58ce0ca8422609b68eb0adfcf..6d00dff5f965509a3156d178533e1044a4ff5cfa 100644 --- a/apollo-portal/src/main/resources/static/cluster.html +++ b/apollo-portal/src/main/resources/static/cluster.html @@ -70,7 +70,7 @@
- +
diff --git a/apollo-portal/src/main/resources/static/config.html b/apollo-portal/src/main/resources/static/config.html index 8ca9cbe42bc02f70f9b7c83154f19b4a97309484..5c756f1da956e5fbdc48439ee51a4f1763af3c75 100644 --- a/apollo-portal/src/main/resources/static/config.html +++ b/apollo-portal/src/main/resources/static/config.html @@ -225,7 +225,7 @@
@@ -308,7 +308,7 @@ diff --git a/apollo-portal/src/main/resources/static/config/sync.html b/apollo-portal/src/main/resources/static/config/sync.html index fe8267c07255fd7ab446f484bbfe1187ad8d5751..be69c0f7d7f9ea645136ba39e8dd1e1feb3b918a 100644 --- a/apollo-portal/src/main/resources/static/config/sync.html +++ b/apollo-portal/src/main/resources/static/config/sync.html @@ -32,7 +32,7 @@ ng-click="diff()">下一步 + diff --git a/apollo-portal/src/main/resources/static/namespace/role.html b/apollo-portal/src/main/resources/static/namespace/role.html index 015d3645ece72d67331078424e76cc8edf15d51f..95d73009afc6b5688c827dd829b27d0a484e568d 100644 --- a/apollo-portal/src/main/resources/static/namespace/role.html +++ b/apollo-portal/src/main/resources/static/namespace/role.html @@ -40,7 +40,7 @@
- +
@@ -70,7 +70,7 @@
- +
diff --git a/apollo-portal/src/main/resources/static/scripts/controller/AppController.js b/apollo-portal/src/main/resources/static/scripts/controller/AppController.js index 6d6cc87a95f46c15dc96dfde0977ad80328787d5..ed4a5f91fd445f70addea52800d30e8f030107bd 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/AppController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/AppController.js @@ -1,6 +1,8 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toastr', 'AppService', 'UserService', 'AppUtil', 'OrganizationService', function ($scope, $window, toastr, AppService, UserService, AppUtil, OrganizationService) { + $scope.submitBtnDisabled = false; + OrganizationService.find_organizations().then(function (result) { var organizations = []; result.forEach(function (item) { @@ -47,12 +49,15 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast } $scope.app.ownerName = user.id; + $scope.submitBtnDisabled = true; AppService.create($scope.app).then(function (result) { toastr.success('添加成功!'); setInterval(function () { + $scope.submitBtnDisabled = false; $window.location.href = '/config.html?#appid=' + result.appId; }, 1000); }, function (result) { + $scope.submitBtnDisabled = false; toastr.error(AppUtil.errorMsg(result), '添加失败!'); }); }; diff --git a/apollo-portal/src/main/resources/static/scripts/controller/ClusterController.js b/apollo-portal/src/main/resources/static/scripts/controller/ClusterController.js index f1962f482539faaedd6ce0901c965d81eda2a306..940da91bc67198d7eb6ce60c68a4e9e3277ac71c 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/ClusterController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/ClusterController.js @@ -9,6 +9,8 @@ cluster_module.controller('ClusterController', $scope.step = 1; + $scope.submitBtnDisabled = false; + EnvService.find_all_envs().then(function (result) { $scope.envs = []; result.forEach(function (env) { @@ -35,6 +37,7 @@ cluster_module.controller('ClusterController', $scope.envs.forEach(function (env) { if (env.checked) { noEnvChecked = false; + $scope.submitBtnDisabled = true; ClusterService.create_cluster($scope.appId, env.name, { name: $scope.clusterName, @@ -42,8 +45,10 @@ cluster_module.controller('ClusterController', }).then(function (result) { toastr.success(env.name, "集群创建成功"); $scope.step = 2; + $scope.submitBtnDisabled = false; }, function (result) { toastr.error(AppUtil.errorMsg(result), "集群创建失败"); + $scope.submitBtnDisabled = false; }) } }); diff --git a/apollo-portal/src/main/resources/static/scripts/controller/NamespaceController.js b/apollo-portal/src/main/resources/static/scripts/controller/NamespaceController.js index db83d6cb3d66fa14ee543abfeafc73c1d297c958..99f59328a180587e01fc4532fd097b667e298946 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/NamespaceController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/NamespaceController.js @@ -10,6 +10,8 @@ namespace_module.controller("LinkNamespaceController", $scope.step = 1; + $scope.submitBtnDisabled = false; + PermissionService.has_root_permission().then(function (result) { $scope.hasRootPermission = result.hasPermission; }); @@ -90,23 +92,29 @@ namespace_module.controller("LinkNamespaceController", } }); }); + + $scope.submitBtnDisabled = true; NamespaceService.createNamespace($scope.appId, namespaceCreationModels) .then(function (result) { toastr.success("创建成功"); $scope.step = 2; setInterval(function () { + $scope.submitBtnDisabled = false; $window.location.href = '/namespace/role.html?#appid=' + $scope.appId + "&namespaceName=" + $scope.namespaceName; }, 1000); }, function (result) { + $scope.submitBtnDisabled = false; toastr.error(AppUtil.errorMsg(result)); }); } else { + $scope.submitBtnDisabled = true; NamespaceService.createAppNamespace($scope.appId, $scope.appNamespace).then( function (result) { $scope.step = 2; setInterval(function () { + $scope.submitBtnDisabled = false; if ($scope.appNamespace.isPublic) { $window.location.reload(); } else {//private的直接link并且跳转到授权页面 @@ -116,6 +124,7 @@ namespace_module.controller("LinkNamespaceController", } }, 1000); }, function (result) { + $scope.submitBtnDisabled = false; toastr.error(AppUtil.errorMsg(result), "创建失败"); }); } diff --git a/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js b/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js index 9dd3e7a6a4be8065729f4d4db8c3d9adbf64ae37..c7020cbc224001fcb62476e7604bc6c27c981975 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/config/ConfigNamespaceController.js @@ -38,7 +38,12 @@ application_module.controller("ConfigNamespaceController", $scope.createItem = createItem; $scope.doItem = doItem; - + + + $scope.releaseBtnDisabled = false; + $scope.addItemBtnDisabled = false; + $scope.commitChangeBtnDisabled = false; + PermissionService.get_app_role_users($rootScope.pageContext.appId) .then(function (result) { @@ -91,6 +96,12 @@ application_module.controller("ConfigNamespaceController", namespaceId: namespace.namespace.id, format: namespace.format }; + + //prevent repeat submit + if ($scope.commitChangeBtnDisabled){ + return; + } + $scope.commitChangeBtnDisabled = true; ConfigService.modify_items($rootScope.pageContext.appId, $rootScope.pageContext.env, $rootScope.pageContext.clusterName, namespace.namespace.namespaceName, @@ -99,14 +110,17 @@ application_module.controller("ConfigNamespaceController", toastr.success("更新成功, 如需生效请发布"); //refresh all namespace items $rootScope.refreshNamespaces(); + $scope.commitChangeBtnDisabled = false; return true; }, function (result) { toastr.error(AppUtil.errorMsg(result), "更新失败"); + $scope.commitChangeBtnDisabled = false; return false; } ); } + var releaseModal = $('#releaseModal'); $scope.toReleaseNamespace = {}; function prepareReleaseNamespace(namespace) { @@ -125,6 +139,7 @@ application_module.controller("ConfigNamespaceController", $scope.releaseComment = ''; function release() { + $scope.releaseBtnDisabled = true; ReleaseService.release($rootScope.pageContext.appId, $rootScope.pageContext.env, $rootScope.pageContext.clusterName, $scope.toReleaseNamespace.namespace.namespaceName, @@ -134,9 +149,11 @@ application_module.controller("ConfigNamespaceController", releaseModal.modal('hide'); toastr.success("发布成功"); //refresh all namespace items + $scope.releaseBtnDisabled = false; $rootScope.refreshNamespaces(); }, function (result) { + $scope.releaseBtnDisabled = false; toastr.error(AppUtil.errorMsg(result), "发布失败"); } @@ -240,6 +257,7 @@ application_module.controller("ConfigNamespaceController", return; } + $scope.addItemBtnDisabled = true; ConfigService.create_item($rootScope.pageContext.appId, cluster.env, cluster.name, @@ -249,8 +267,10 @@ application_module.controller("ConfigNamespaceController", toastr.success(cluster.env + " , " + $scope.item.key, "添加成功"); itemModal.modal('hide'); + $scope.addItemBtnDisabled = false; $rootScope.refreshNamespaces(namespace_view_type.TABLE); }, function (result) { + $scope.addItemBtnDisabled = false; toastr.error(AppUtil.errorMsg(result), "添加失败"); }); diff --git a/apollo-portal/src/main/resources/static/scripts/controller/config/SyncConfigController.js b/apollo-portal/src/main/resources/static/scripts/controller/config/SyncConfigController.js index 41dd9203c10c710382ea3f70eb54ca28bcfe86e6..a80d394c78dab79fba9bae6412ed2db74e6c7389 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/config/SyncConfigController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/config/SyncConfigController.js @@ -11,6 +11,7 @@ sync_item_module.controller("SyncItemController", }; + $scope.syncBtnDisabled = false; ////// load items ////// ConfigService.find_items($scope.pageContext.appId, $scope.pageContext.env, @@ -51,15 +52,20 @@ sync_item_module.controller("SyncItemController", toastr.error(AppUtil.errorMsg(result)); }); }; - + $scope.syncItems = function () { - ConfigService.sync_items($scope.pageContext.appId, $scope.pageContext.namespaceName, parseSyncSourceData()).then(function (result) { - $scope.syncItemStep += 1; - $scope.syncSuccess = true; - }, function (result) { - $scope.syncSuccess = false; - toastr.error(AppUtil.errorMsg(result)); - }); + $scope.syncBtnDisabled = true; + ConfigService.sync_items($scope.pageContext.appId, + $scope.pageContext.namespaceName, + parseSyncSourceData()).then(function (result) { + $scope.syncItemStep += 1; + $scope.syncSuccess = true; + $scope.syncBtnDisabled = false; + }, function (result) { + $scope.syncSuccess = false; + $scope.syncBtnDisabled = false; + toastr.error(AppUtil.errorMsg(result)); + }); }; var selectedClusters = []; diff --git a/apollo-portal/src/main/resources/static/scripts/controller/role/AppRoleController.js b/apollo-portal/src/main/resources/static/scripts/controller/role/AppRoleController.js index eacd1fee64dcbd61209448a0c2767773cb473f7c..7635cdc2d40788ebf0f91da8d743d8fa32eccb4c 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/role/AppRoleController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/role/AppRoleController.js @@ -7,6 +7,8 @@ role_module.controller('AppRoleController', appId: params.appid }; + $scope.submitBtnDisabled = false; + $scope.userSelectWidgetId = 'toAssignMasterRoleUser'; PermissionService.has_assign_user_permission($scope.pageContext.appId) @@ -31,13 +33,16 @@ role_module.controller('AppRoleController', return; } var toAssignMasterRoleUser = user.id; + $scope.submitBtnDisabled = true; PermissionService.assign_master_role($scope.pageContext.appId, toAssignMasterRoleUser) .then(function (result) { + $scope.submitBtnDisabled = false; toastr.success("添加成功"); $scope.appRoleUsers.masterUsers.push({userId: toAssignMasterRoleUser}); $('.' + $scope.userSelectWidgetId).select2("val", ""); }, function (result) { + $scope.submitBtnDisabled = false; toastr.error(AppUtil.errorMsg(result), "添加失败"); }); }; diff --git a/apollo-portal/src/main/resources/static/scripts/controller/role/NamespaceRoleController.js b/apollo-portal/src/main/resources/static/scripts/controller/role/NamespaceRoleController.js index 66900fdf6c2fe9ccd15da154035f683871a66f05..d4f8f9f019906711dfc7d492f1b1a0362c03600e 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/role/NamespaceRoleController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/role/NamespaceRoleController.js @@ -10,6 +10,9 @@ role_module.controller('NamespaceRoleController', namespaceName: params.namespaceName }; + $scope.modifyRoleSubmitBtnDisabled = false; + $scope.ReleaseRoleSubmitBtnDisabled = false; + $scope.releaseRoleWidgetId = 'releaseRoleWidgetId'; $scope.modifyRoleWidgetId = 'modifyRoleWidgetId'; @@ -35,16 +38,19 @@ role_module.controller('NamespaceRoleController', toastr.warning("请选择用户"); return; } + $scope.ReleaseRoleSubmitBtnDisabled = true; var toAssignReleaseNamespaceRoleUser = user.id; PermissionService.assign_release_namespace_role($scope.pageContext.appId, $scope.pageContext.namespaceName, toAssignReleaseNamespaceRoleUser) .then(function (result) { toastr.success("添加成功"); + $scope.ReleaseRoleSubmitBtnDisabled = false; $scope.rolesAssignedUsers.releaseRoleUsers.push( {userId: toAssignReleaseNamespaceRoleUser}); $('.' + $scope.releaseRoleWidgetId).select2("val", ""); }, function (result) { + $scope.ReleaseRoleSubmitBtnDisabled = false; toastr.error(AppUtil.errorMsg(result), "添加失败"); }); } else { @@ -53,16 +59,19 @@ role_module.controller('NamespaceRoleController', toastr.warning("请选择用户"); return; } + $scope.modifyRoleSubmitBtnDisabled = true; var toAssignModifyNamespaceRoleUser = user.id; PermissionService.assign_modify_namespace_role($scope.pageContext.appId, $scope.pageContext.namespaceName, toAssignModifyNamespaceRoleUser) .then(function (result) { toastr.success("添加成功"); + $scope.modifyRoleSubmitBtnDisabled = false; $scope.rolesAssignedUsers.modifyRoleUsers.push( {userId: toAssignModifyNamespaceRoleUser}); $('.' + $scope.modifyRoleWidgetId).select2("val", ""); }, function (result) { + $scope.modifyRoleSubmitBtnDisabled = false; toastr.error(AppUtil.errorMsg(result), "添加失败"); }); } diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/AllTests.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/AllTests.java index f3b62e68b54e0522020272d4c5488d01bd0419b9..1989754f1abeb9a2a3777077517c1f6dca48022b 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/AllTests.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/AllTests.java @@ -17,7 +17,8 @@ import org.junit.runners.Suite.SuiteClasses; @SuiteClasses({ ConfigServiceTest.class, PropertyResolverTest.class, NamespaceServiceTest.class, ServiceExceptionTest.class, RolePermissionServiceTest.class, - AppNamespaceServiceTest.class, RoleInitializationServiceTest.class, FileTextResolverTest.class + AppNamespaceServiceTest.class, RoleInitializationServiceTest.class, FileTextResolverTest.class, + RetryableRestTemplateTest.class }) public class AllTests { diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/RetryableRestTemplateTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/RetryableRestTemplateTest.java new file mode 100644 index 0000000000000000000000000000000000000000..13378584a6e72ef01b6479989529fe9983d1977d --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/RetryableRestTemplateTest.java @@ -0,0 +1,146 @@ +package com.ctrip.framework.apollo.portal; + +import com.ctrip.framework.apollo.core.dto.ServiceDTO; +import com.ctrip.framework.apollo.core.enums.Env; +import com.ctrip.framework.apollo.core.exception.ServiceException; +import com.ctrip.framework.apollo.portal.api.AdminServiceAddressLocator; +import com.ctrip.framework.apollo.portal.api.RetryableRestTemplate; + +import org.apache.http.HttpHost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.HttpHostConnectException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.client.RestTemplate; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.util.Arrays; +import java.util.Collections; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class RetryableRestTemplateTest extends AbstractUnitTest { + + @Mock + private AdminServiceAddressLocator serviceAddressLocator; + @Mock + private RestTemplate restTemplate; + @InjectMocks + private RetryableRestTemplate retryableRestTemplate; + + private String path = "app"; + private String serviceOne = "http://10.0.0.1"; + private String serviceTwo = "http://10.0.0.2"; + private String serviceThree = "http://10.0.0.3"; + private ResourceAccessException socketTimeoutException = new ResourceAccessException(""); + private ResourceAccessException httpHostConnectException = new ResourceAccessException(""); + private ResourceAccessException connectTimeoutException = new ResourceAccessException(""); + private Object request = new Object(); + private ResponseEntity entity = new ResponseEntity<>(HttpStatus.OK); + + + @Before + public void init() { + socketTimeoutException.initCause(new SocketTimeoutException()); + httpHostConnectException + .initCause(new HttpHostConnectException(new HttpHost(serviceOne, 80), new ConnectException())); + connectTimeoutException.initCause(new ConnectTimeoutException()); + } + + @Test(expected = ServiceException.class) + public void testNoAdminServer() { + + when(serviceAddressLocator.getServiceList(any())).thenReturn(Collections.emptyList()); + + retryableRestTemplate.get(Env.DEV, path, Object.class); + } + + @Test(expected = ServiceException.class) + public void testAllServerDown() { + + when(serviceAddressLocator.getServiceList(any())) + .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); + when(restTemplate.getForObject(serviceOne + "/" + path, Object.class)).thenThrow(socketTimeoutException); + when(restTemplate.getForObject(serviceTwo + "/" + path, Object.class)).thenThrow(httpHostConnectException); + when(restTemplate.getForObject(serviceThree + "/" + path, Object.class)).thenThrow(connectTimeoutException); + + retryableRestTemplate.get(Env.DEV, path, Object.class); + + verify(restTemplate).getForObject(serviceOne + "/" + path, Object.class); + verify(restTemplate).getForObject(serviceTwo + "/" + path, Object.class); + verify(restTemplate).getForObject(serviceThree + "/" + path, Object.class); + + + } + + @Test + public void testOneServerDown() { + + Object result = new Object(); + when(serviceAddressLocator.getServiceList(any())) + .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); + when(restTemplate.getForObject(serviceOne + "/" + path, Object.class)).thenThrow(socketTimeoutException); + when(restTemplate.getForObject(serviceTwo + "/" + path, Object.class)).thenReturn(result); + when(restTemplate.getForObject(serviceThree + "/" + path, Object.class)).thenThrow(connectTimeoutException); + + Object o = retryableRestTemplate.get(Env.DEV, path, Object.class); + + verify(restTemplate).getForObject(serviceOne + "/" + path, Object.class); + verify(restTemplate).getForObject(serviceTwo + "/" + path, Object.class); + verify(restTemplate, times(0)).getForObject(serviceThree + "/" + path, Object.class); + Assert.assertEquals(result, o); + } + + @Test(expected = ResourceAccessException.class) + public void testPostSocketTimeoutNotRetry(){ + when(serviceAddressLocator.getServiceList(any())) + .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); + + when(restTemplate.postForEntity(serviceOne + "/" + path, request, Object.class)).thenThrow(socketTimeoutException); + when(restTemplate.postForEntity(serviceTwo + "/" + path, request, Object.class)).thenReturn(entity); + + retryableRestTemplate.post(Env.DEV, path, request, Object.class); + + verify(restTemplate).postForEntity(serviceOne + "/" + path, request, Object.class); + verify(restTemplate, times(0)).postForEntity(serviceTwo + "/" + path, request, Object.class); + } + + + @Test + public void testDelete(){ + when(serviceAddressLocator.getServiceList(any())) + .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); + + retryableRestTemplate.delete(Env.DEV, path); + + verify(restTemplate).delete(serviceOne + "/" + path); + + } + + @Test + public void testPut(){ + when(serviceAddressLocator.getServiceList(any())) + .thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree))); + + retryableRestTemplate.put(Env.DEV, path, request); + + verify(restTemplate).put(serviceOne + "/" + path, request); + } + + private ServiceDTO mockService(String homeUrl) { + ServiceDTO serviceDTO = new ServiceDTO(); + serviceDTO.setHomepageUrl(homeUrl); + return serviceDTO; + } + +} diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/auth/CtripUserServiceTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserServiceTest.java similarity index 95% rename from apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/auth/CtripUserServiceTest.java rename to apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserServiceTest.java index 4cbdb7cb985cc011e12eef5ff50621fdeb63e6a2..827abcca4cf10c0df57e9f78534a28f75895b1f5 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/auth/CtripUserServiceTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/auth/ctrip/CtripUserServiceTest.java @@ -1,16 +1,16 @@ -package com.ctrip.framework.apollo.portal.auth; +package com.ctrip.framework.apollo.portal.auth.ctrip; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import com.ctrip.framework.apollo.portal.AbstractUnitTest; +import com.ctrip.framework.apollo.portal.auth.ctrip.CtripUserService; import com.ctrip.framework.apollo.portal.entity.po.UserInfo; import com.ctrip.framework.apollo.portal.service.ServerConfigService; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; @@ -32,8 +32,7 @@ import static org.mockito.Mockito.when; /** * @author Jason Song(song_s@ctrip.com) */ -@RunWith(MockitoJUnitRunner.class) -public class CtripUserServiceTest { +public class CtripUserServiceTest extends AbstractUnitTest{ private CtripUserService ctripUserService; private String someUserServiceUrl; private String someUserServiceToken; @@ -48,6 +47,8 @@ public class CtripUserServiceTest { @Before public void setUp() throws Exception { + when(serverConfigService.getValue("api.connectTimeout", "3000")).thenReturn("3000"); + when(serverConfigService.getValue("api.readTimeout", "3000")).thenReturn("3000"); ctripUserService = new CtripUserService(serverConfigService); ReflectionTestUtils.setField(ctripUserService, "restTemplate", restTemplate); someResponseType = @@ -58,6 +59,7 @@ public class CtripUserServiceTest { someUserServiceToken = "someToken"; when(serverConfigService.getValue("userService.url")).thenReturn(someUserServiceUrl); when(serverConfigService.getValue("userService.accessToken")).thenReturn(someUserServiceToken); + } @Test