提交 1cc43d08 编写于 作者: Y Yiming Liu

Integrate env & service discovery in client

上级 bde0a061
......@@ -3,10 +3,12 @@ spring:
name: apollo-adminservice
server:
port: ${port:8080}
port: ${port:23646}
logging:
level:
org.springframework.cloud: 'DEBUG'
file: /opt/logs/apollo-adminservice.log
ctrip:
appid: 100003172
......@@ -39,13 +39,6 @@
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
<!-- log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- end of log -->
<!-- test -->
<dependency>
<groupId>commons-logging</groupId>
......
......@@ -4,7 +4,8 @@ package com.ctrip.apollo.client.constants;
* @author Jason Song(song_s@ctrip.com)
*/
public class Constants {
public static final String APP_ID = "appId";
public static final String APP_ID = "app.id";
public static final String VERSION = "version";
public static final String DEFAULT_VERSION_NAME = "latest-release";
public static final String ENV = "env";
}
package com.ctrip.apollo.client.env;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.utils.StringUtils;
public class ClientEnvironment {
private static final Logger logger = LoggerFactory.getLogger(ClientEnvironment.class);
private final static String DEFAULT_FILE = "/apollo.properties";
private AtomicReference<Env> env = new AtomicReference<Env>();
private static ClientEnvironment instance = new ClientEnvironment();
private ClientEnvironment() {
}
public static ClientEnvironment getInstance() {
return instance;
}
public Env getEnv() {
if (env.get() == null) {
Env resultEnv = Apollo.getEnv();
Properties apolloProperties = null;
try {
apolloProperties = readConfigFile(DEFAULT_FILE, null);
} catch (IOException e) {
throw new IllegalArgumentException("Could not read Apollo properties");
}
if (apolloProperties != null) {
String strEnv = apolloProperties.getProperty(Constants.ENV);
if (!StringUtils.isBlank(strEnv)) {
resultEnv = Env.valueOf(strEnv.trim().toUpperCase());
}
}
env.compareAndSet(null, resultEnv);
}
if (env.get() == null) {
throw new IllegalArgumentException("Apollo env is not set");
}
return env.get();
}
public String getMetaServerDomainName() {
return MetaDomainConsts.getDomain(getEnv());
}
@SuppressWarnings("unchecked")
private Properties readConfigFile(String configPath, Properties defaults) throws IOException {
InputStream in = this.getClass().getResourceAsStream(configPath);
logger.info("Reading config from resource {}", configPath);
if (in == null) {
// load outside resource under current user path
Path path = new File(System.getProperty("user.dir") + configPath).toPath();
if (Files.isReadable(path)) {
in = new FileInputStream(path.toFile());
logger.info("Reading config from file {} ", path);
}
}
Properties props = new Properties();
if (defaults != null) {
props.putAll(defaults);
}
if (in != null) {
props.load(in);
}
StringBuilder sb = new StringBuilder();
for (Enumeration<String> e = (Enumeration<String>) props.propertyNames(); e
.hasMoreElements();) {
String key = e.nextElement();
String val = (String) props.getProperty(key);
sb.append(key).append('=').append(val).append('\n');
}
logger.info("Reading properties: \n" + sb.toString());
return props;
}
}
......@@ -2,6 +2,8 @@ package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.loader.impl.InMemoryConfigLoader;
import com.ctrip.apollo.client.loader.impl.LocalFileConfigLoader;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoader;
import com.ctrip.apollo.client.util.ConfigUtil;
import org.springframework.web.client.RestTemplate;
......@@ -10,14 +12,13 @@ import org.springframework.web.client.RestTemplate;
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigLoaderFactory {
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private ConfigLoaderFactory() {
}
private ConfigLoaderFactory() {}
public static ConfigLoaderFactory getInstance() {
return configLoaderFactory;
}
public static ConfigLoaderFactory getInstance() {
return configLoaderFactory;
}
public ConfigLoader getLocalFileConfigLoader() {
ConfigLoader configLoader = new LocalFileConfigLoader();
......@@ -31,12 +32,17 @@ public class ConfigLoaderFactory {
}
public ConfigLoader getRemoteConfigLoader() {
ConfigLoader remoteConfigLoader = new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance());
ConfigLoader remoteConfigLoader = new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance(), new ConfigServiceLocator());
// remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader());
return remoteConfigLoader;
}
public ConfigLoaderManager getConfigLoaderManager() {
ClientEnvironment env = ClientEnvironment.getInstance();
if (env.getEnv().equals(Env.LOCAL)) {
return new ConfigLoaderManager(getLocalFileConfigLoader(), ConfigUtil.getInstance());
} else {
return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance());
}
}
}
package com.ctrip.apollo.client.loader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.core.serivce.ApolloService;
public class ConfigServiceLocator {
private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class);
private RestTemplate restTemplate = new RestTemplate();
private List<ApolloService> serviceCaches = new ArrayList<>();
public List<ApolloService> getConfigServices() {
ClientEnvironment env = ClientEnvironment.getInstance();
String domainName = env.getMetaServerDomainName();
String url = domainName + "/services/config";
try {
ApolloService[] services = restTemplate.getForObject(new URI(url), ApolloService[].class);
if (services != null && services.length > 0) {
serviceCaches.clear();
for (ApolloService service : services) {
serviceCaches.add(service);
}
}
} catch (Exception e) {
logger.warn(e.getMessage());
}
return serviceCaches;
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -13,6 +15,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
/**
......@@ -24,10 +27,12 @@ public class RemoteConfigLoader extends AbstractConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class);
private final RestTemplate restTemplate;
private final ConfigUtil configUtil;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil) {
private final ConfigServiceLocator serviceLocator;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil, ConfigServiceLocator locator) {
this.restTemplate = restTemplate;
this.configUtil = configUtil;
this.serviceLocator = locator;
}
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, String cluster, ApolloRegistry apolloRegistry, ApolloConfig previousConfig) {
......@@ -78,9 +83,17 @@ public class RemoteConfigLoader extends AbstractConfigLoader {
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
ApolloConfig result = this.getRemoteConfig(restTemplate,
configUtil.getConfigServerUrl(), configUtil.getCluster(),
getConfigServiceUrl(), configUtil.getCluster(),
apolloRegistry, previous);
//When remote server return 304, we need to return the previous result
return result == null ? previous : result;
}
}
private String getConfigServiceUrl() {
List<ApolloService> services = serviceLocator.getConfigServices();
if(services.size()==0){
throw new RuntimeException("No available config service");
}
return services.get(0).getHomepageUrl();
}
}
\ No newline at end of file
......@@ -23,7 +23,7 @@ public class ConfigUtil {
public static final String APOLLO_PROPERTY = "apollo.properties";
private static ConfigUtil configUtil = new ConfigUtil();
private ApplicationContext applicationContext;
private ConfigUtil() {
}
......@@ -31,11 +31,6 @@ public class ConfigUtil {
return configUtil;
}
public String getConfigServerUrl() {
// TODO return the meta server url based on different environments
return "http://localhost";
}
public String getCluster() {
// TODO return the actual cluster
return "default";
......
appId=101
app.id=101
version=1.0
env=local
\ No newline at end of file
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
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.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService;
/**
* @author Jason Song(song_s@ctrip.com)
......@@ -35,12 +41,14 @@ public class RemoteConfigLoaderTest {
private RestTemplate restTemplate;
private ConfigUtil configUtil;
@Mock
private ConfigServiceLocator serviceLocater;
@Mock
private ResponseEntity<ApolloConfig> someResponse;
@Before
public void setUp() {
configUtil = spy(ConfigUtil.getInstance());
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil));
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil, serviceLocater));
}
@Test
......@@ -52,7 +60,12 @@ public class RemoteConfigLoaderTest {
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
when(configUtil.getConfigServerUrl()).thenReturn(someServerUrl);
ApolloService someService = new ApolloService();
someService.setHomepageUrl(someServerUrl);
List<ApolloService> someServices = new ArrayList<>();
someServices.add(someService);
when(serviceLocater.getConfigServices()).thenReturn(someServices);
when(configUtil.getCluster()).thenReturn(someCluster);
doReturn(apolloConfig).when(remoteConfigLoader)
.getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig);
......@@ -115,7 +128,6 @@ public class RemoteConfigLoaderTest {
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
assertNull(result);
}
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
......
package com.ctrip.apollo.metaservice.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.core.serivce.ApolloService;
import com.ctrip.apollo.metaservice.service.DiscoveryService;
import com.netflix.appinfo.InstanceInfo;
......@@ -18,17 +20,44 @@ public class ServiceController {
@RequestMapping("/meta")
public List<InstanceInfo> getMetaService() {
return discoveryService.getMetaServiceInstances();
public List<ApolloService> getMetaService() {
List<InstanceInfo> instances = discoveryService.getMetaServiceInstances();
List<ApolloService> result = new ArrayList<ApolloService>();
for (InstanceInfo instance : instances) {
ApolloService service = new ApolloService();
service.setAppName(instance.getAppName());
service.setInstanceId(instance.getInstanceId());
service.setHomepageUrl(instance.getHomePageUrl());
result.add(service);
}
return result;
}
@RequestMapping("/config")
public List<InstanceInfo> getConfigService() {
return discoveryService.getConfigServiceInstances();
public List<ApolloService> getConfigService() {
List<InstanceInfo> instances = discoveryService.getConfigServiceInstances();
List<ApolloService> result = new ArrayList<ApolloService>();
for (InstanceInfo instance : instances) {
ApolloService service = new ApolloService();
service.setAppName(instance.getAppName());
service.setInstanceId(instance.getInstanceId());
service.setHomepageUrl(instance.getHomePageUrl());
result.add(service);
}
return result;
}
@RequestMapping("/admin")
public List<InstanceInfo> getAdminService(){
return discoveryService.getAdminServiceInstances();
public List<ApolloService> getAdminService() {
List<InstanceInfo> instances = discoveryService.getAdminServiceInstances();
List<ApolloService> result = new ArrayList<ApolloService>();
for (InstanceInfo instance : instances) {
ApolloService service = new ApolloService();
service.setAppName(instance.getAppName());
service.setInstanceId(instance.getInstanceId());
service.setHomepageUrl(instance.getHomePageUrl());
result.add(service);
}
return result;
}
}
......@@ -6,7 +6,7 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ctrip.apollo.core.ServiceIdConsts;
import com.ctrip.apollo.core.ServiceNameConsts;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
......@@ -18,17 +18,17 @@ public class DiscoveryService {
private EurekaClient eurekaClient;
public List<InstanceInfo> getConfigServiceInstances() {
Application application = eurekaClient.getApplication(ServiceIdConsts.APOLLO_CONFIGSERVICE);
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_CONFIGSERVICE);
return application != null ? application.getInstances() : new ArrayList<>();
}
public List<InstanceInfo> getMetaServiceInstances() {
Application application = eurekaClient.getApplication(ServiceIdConsts.APOLLO_METASERVICE);
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_METASERVICE);
return application != null ? application.getInstances() : new ArrayList<>();
}
public List<InstanceInfo> getAdminServiceInstances(){
Application application = eurekaClient.getApplication(ServiceIdConsts.APOLLO_ADMINSERVICE);
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_ADMINSERVICE);
return application != null ? application.getInstances() : new ArrayList<>();
}
}
......@@ -3,10 +3,12 @@ spring:
name: apollo-configservice
server:
port: ${port:80}
port: ${port:2756}
logging:
level:
org.springframework.cloud: 'DEBUG'
file: /opt/logs/apollo-configservice.log
ctrip:
appid: 100003171
\ No newline at end of file
......@@ -29,5 +29,11 @@
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
<!-- log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- end of log -->
</dependencies>
</project>
package com.ctrip.apollo;
public class Apollo {
public final static String VERSION = "java-0.0.1-SNAPSHOT";
private static Env m_env;
public enum Env {
LOCAL, DEV, FWS, FAT, UAT, LPT, PROD, TOOLS
}
public static void initialize(Env env) {
m_env = env;
}
public static Env getEnv() {
return m_env;
}
}
package com.ctrip.apollo.core;
import java.util.HashMap;
import java.util.Map;
import com.ctrip.apollo.Apollo.Env;
public class MetaDomainConsts {
public static final String LOCAL = "http://localhost";
public static final String DEV = "http://ws.meta.apollo.fx.dev.nt.ctripcorp.com";
public static final String FAT = "http://ws.meta.apollo.fx.fat.nt.ctripcorp.com";;
public static final String FWS = "http://ws.meta.apollo.fx.fws.nt.ctripcorp.com";
public static final String UAT = "http://ws.meta.apollo.fx.uat.nt.ctripcorp.com";
public static final String LPT = "http://ws.meta.apollo.fx.lpt.nt.ctripcorp.com";
public static final String TOOLS = "http://ws.meta.apollo.fx.tools.ctripcorp.com";
public static final String PRD = "http://ws.meta.apollo.fx.ctripcorp.com";
private static Map<Env, String> domains = new HashMap<>();
static {
domains.put(Env.LOCAL, LOCAL);
domains.put(Env.DEV, DEV);
domains.put(Env.FAT, FAT);
domains.put(Env.FWS, FWS);
domains.put(Env.UAT, UAT);
domains.put(Env.LPT, LPT);
domains.put(Env.TOOLS, TOOLS);
domains.put(Env.PROD, PRD);
}
public static String getDomain(Env env) {
return domains.get(env);
}
}
package com.ctrip.apollo.core;
public class ServiceIdConsts {
public class ServiceNameConsts {
public static final String APOLLO_METASERVICE = "apollo-metaservice";
......
package com.ctrip.apollo.core.serivce;
public class ApolloService {
private String appName;
private String instanceId;
private String homepageUrl;
public String getAppName() {
return appName;
}
public String getHomepageUrl() {
return homepageUrl;
}
public String getInstanceId() {
return instanceId;
}
public void setAppName(String appName) {
this.appName = appName;
}
public void setHomepageUrl(String homepageUrl) {
this.homepageUrl = homepageUrl;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
}
package com.ctrip.apollo.core.utils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApolloThreadFactory implements ThreadFactory {
private static Logger log = LoggerFactory.getLogger(ApolloThreadFactory.class);
private final AtomicLong m_threadNumber = new AtomicLong(1);
private final String m_namePrefix;
private final boolean m_daemon;
private final static ThreadGroup m_threadGroup = new ThreadGroup("Apollo");
public static ThreadGroup getThreadGroup() {
return m_threadGroup;
}
public static ThreadFactory create(String namePrefix, boolean daemon) {
return new ApolloThreadFactory(namePrefix, daemon);
}
public static boolean waitAllShutdown(int timeoutInMillis) {
ThreadGroup group = getThreadGroup();
Thread[] activeThreads = new Thread[group.activeCount()];
group.enumerate(activeThreads);
Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads));
Set<Thread> dies = new HashSet<Thread>();
log.info("Current ACTIVE thread count is: {}", alives.size());
long expire = System.currentTimeMillis() + timeoutInMillis;
while (System.currentTimeMillis() < expire) {
classify(alives, dies, new ClassifyStandard<Thread>() {
@Override
public boolean satisfy(Thread t) {
return !t.isAlive() || t.isInterrupted() || t.isDaemon();
}
});
if (alives.size() > 0) {
log.info("Alive apollo threads: {}", alives);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
// ignore
}
} else {
log.info("All apollo threads are shutdown.");
return true;
}
}
log.warn("Some apollo threads are still alive but expire time has reached, alive threads: {}", alives);
return false;
}
private static interface ClassifyStandard<T> {
boolean satisfy(T t);
}
private static <T> void classify(Set<T> src, Set<T> des, ClassifyStandard<T> standard) {
Set<T> s = new HashSet<>();
for (T t : src) {
if (standard.satisfy(t)) {
s.add(t);
}
}
src.removeAll(s);
des.addAll(s);
}
private ApolloThreadFactory(String namePrefix, boolean daemon) {
m_namePrefix = namePrefix;
m_daemon = daemon;
}
public Thread newThread(Runnable r) {
Thread t = new Thread(m_threadGroup, r,//
m_threadGroup.getName() + "-" + m_namePrefix + "-" + m_threadNumber.getAndIncrement());
t.setDaemon(m_daemon);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
package com.ctrip.apollo.core.utils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
public class DNSUtil {
public static List<String> resolve(String domainName) throws UnknownHostException {
List<String> result = new ArrayList<String>();
InetAddress[] addresses = InetAddress.getAllByName(domainName);
if (addresses != null) {
for (InetAddress addr : addresses) {
result.add(addr.getHostAddress());
}
}
return result;
}
}
package com.ctrip.apollo.core.utils;
import java.util.Collection;
import java.util.Iterator;
public class StringUtils {
public static final String EMPTY = "";
/**
* <p>
* Checks if a String is empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>
* NOTE: This method changed in Lang version 2.0. It no longer trims the String. That functionality is available in isBlank().
* </p>
*
* @param str
* the String to check, may be null
* @return <code>true</code> if the String is empty or null
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
/**
* <p>
* Checks if a String is whitespace, empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
* </pre>
*
* @param str
* the String to check, may be null
* @return <code>true</code> if the String is null, empty or whitespace
*/
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
/**
* <p>
* Removes control characters (char &lt;= 32) from both ends of this String returning <code>null</code> if the String is empty
* ("") after the trim or if it is <code>null</code>.
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToNull(String)}.
* </p>
*
* <pre>
* StringUtils.trimToNull(null) = null
* StringUtils.trimToNull("") = null
* StringUtils.trimToNull(" ") = null
* StringUtils.trimToNull("abc") = "abc"
* StringUtils.trimToNull(" abc ") = "abc"
* </pre>
*
* @param str
* the String to be trimmed, may be null
* @return the trimmed String, <code>null</code> if only chars &lt;= 32, empty or null String input
* @since 2.0
*/
public static String trimToNull(String str) {
String ts = trim(str);
return isEmpty(ts) ? null : ts;
}
/**
* <p>
* Removes control characters (char &lt;= 32) from both ends of this String returning an empty String ("") if the String is empty
* ("") after the trim or if it is <code>null</code>.
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #stripToEmpty(String)}.
* </p>
*
* <pre>
* StringUtils.trimToEmpty(null) = ""
* StringUtils.trimToEmpty("") = ""
* StringUtils.trimToEmpty(" ") = ""
* StringUtils.trimToEmpty("abc") = "abc"
* StringUtils.trimToEmpty(" abc ") = "abc"
* </pre>
*
* @param str
* the String to be trimmed, may be null
* @return the trimmed String, or an empty String if <code>null</code> input
* @since 2.0
*/
public static String trimToEmpty(String str) {
return str == null ? EMPTY : str.trim();
}
/**
* <p>
* Removes control characters (char &lt;= 32) from both ends of this String, handling <code>null</code> by returning
* <code>null</code>.
* </p>
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end characters &lt;= 32. To strip whitespace use
* {@link #strip(String)}.
* </p>
*
* <p>
* To trim your choice of characters, use the {@link #strip(String, String)} methods.
* </p>
*
* <pre>
* StringUtils.trim(null) = null
* StringUtils.trim("") = ""
* StringUtils.trim(" ") = ""
* StringUtils.trim("abc") = "abc"
* StringUtils.trim(" abc ") = "abc"
* </pre>
*
* @param str
* the String to be trimmed, may be null
* @return the trimmed string, <code>null</code> if null String input
*/
public static String trim(String str) {
return str == null ? null : str.trim();
}
/**
* <p>
* Compares two Strings, returning <code>true</code> if they are equal.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive.
* </p>
*
* <pre>
* StringUtils.equals(null, null) = true
* StringUtils.equals(null, "abc") = false
* StringUtils.equals("abc", null) = false
* StringUtils.equals("abc", "abc") = true
* StringUtils.equals("abc", "ABC") = false
* </pre>
*
* @see java.lang.String#equals(Object)
* @param str1
* the first String, may be null
* @param str2
* the second String, may be null
* @return <code>true</code> if the Strings are equal, case sensitive, or both <code>null</code>
*/
public static boolean equals(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
/**
* <p>
* Compares two Strings, returning <code>true</code> if they are equal ignoring the case.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered equal. Comparison is case
* insensitive.
* </p>
*
* <pre>
* StringUtils.equalsIgnoreCase(null, null) = true
* StringUtils.equalsIgnoreCase(null, "abc") = false
* StringUtils.equalsIgnoreCase("abc", null) = false
* StringUtils.equalsIgnoreCase("abc", "abc") = true
* StringUtils.equalsIgnoreCase("abc", "ABC") = true
* </pre>
*
* @see java.lang.String#equalsIgnoreCase(String)
* @param str1
* the first String, may be null
* @param str2
* the second String, may be null
* @return <code>true</code> if the Strings are equal, case insensitive, or both <code>null</code>
*/
public static boolean equalsIgnoreCase(String str1, String str2) {
return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
}
/**
* <p>
* Check if a String starts with a specified prefix.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case sensitive.
* </p>
*
* <pre>
* StringUtils.startsWith(null, null) = true
* StringUtils.startsWith(null, "abc") = false
* StringUtils.startsWith("abcdef", null) = false
* StringUtils.startsWith("abcdef", "abc") = true
* StringUtils.startsWith("ABCDEF", "abc") = false
* </pre>
*
* @see java.lang.String#startsWith(String)
* @param str
* the String to check, may be null
* @param prefix
* the prefix to find, may be null
* @return <code>true</code> if the String starts with the prefix, case sensitive, or both <code>null</code>
* @since 2.4
*/
public static boolean startsWith(String str, String prefix) {
return startsWith(str, prefix, false);
}
/**
* <p>
* Case insensitive check if a String starts with a specified prefix.
* </p>
*
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code> references are considered to be equal. The comparison
* is case insensitive.
* </p>
*
* <pre>
* StringUtils.startsWithIgnoreCase(null, null) = true
* StringUtils.startsWithIgnoreCase(null, "abc") = false
* StringUtils.startsWithIgnoreCase("abcdef", null) = false
* StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
* StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
* </pre>
*
* @see java.lang.String#startsWith(String)
* @param str
* the String to check, may be null
* @param prefix
* the prefix to find, may be null
* @return <code>true</code> if the String starts with the prefix, case insensitive, or both <code>null</code>
* @since 2.4
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
return startsWith(str, prefix, true);
}
/**
* <p>
* Checks if the String contains only unicode digits. A decimal point is not a unicode digit and returns false.
* </p>
*
* <p>
* <code>null</code> will return <code>false</code>. An empty String (length()=0) will return <code>true</code>.
* </p>
*
* <pre>
* StringUtils.isNumeric(null) = false
* StringUtils.isNumeric("") = true
* StringUtils.isNumeric(" ") = false
* StringUtils.isNumeric("123") = true
* StringUtils.isNumeric("12 3") = false
* StringUtils.isNumeric("ab2c") = false
* StringUtils.isNumeric("12-3") = false
* StringUtils.isNumeric("12.3") = false
* </pre>
*
* @param str
* the String to check, may be null
* @return <code>true</code> if only contains digits, and is non-null
*/
public static boolean isNumeric(String str) {
if (str == null) {
return false;
}
int sz = str.length();
for (int i = 0; i < sz; i++) {
if (Character.isDigit(str.charAt(i)) == false) {
return false;
}
}
return true;
}
/**
* <p>
* Check if a String starts with a specified prefix (optionally case insensitive).
* </p>
*
* @see java.lang.String#startsWith(String)
* @param str
* the String to check, may be null
* @param prefix
* the prefix to find, may be null
* @param ignoreCase
* inidicates whether the compare should ignore case (case insensitive) or not.
* @return <code>true</code> if the String starts with the prefix or both <code>null</code>
*/
private static boolean startsWith(String str, String prefix, boolean ignoreCase) {
if (str == null || prefix == null) {
return (str == null && prefix == null);
}
if (prefix.length() > str.length()) {
return false;
}
return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length());
}
public static interface StringFormatter<T> {
public String format(T obj);
}
public static <T> String join(Collection<T> collection, String separator) {
return join(collection, separator, new StringFormatter<T>() {
@Override
public String format(T obj) {
return obj.toString();
}
});
}
public static <T> String join(Collection<T> collection, String separator, StringFormatter<T> formatter) {
Iterator<T> iterator = collection.iterator();
// handle null, zero and one elements before building a buffer
if (iterator == null) {
return null;
}
if (!iterator.hasNext()) {
return EMPTY;
}
T first = iterator.next();
if (!iterator.hasNext()) {
return first == null ? "" : formatter.format(first);
}
// two or more elements
StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
if (first != null) {
buf.append(formatter.format(first));
}
while (iterator.hasNext()) {
buf.append(separator);
T obj = iterator.next();
if (obj != null) {
buf.append(formatter.format(obj));
}
}
return buf.toString();
}
}
......@@ -9,3 +9,5 @@ spring:
username: sa
password: sa
ctrip:
appid: 100003173
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册