mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-9922 Unable to use 8081 port for Load balancing
This commit is contained in:
commit
f66bbb9521
@ -57,7 +57,7 @@ import com.cloud.offering.NetworkOffering;
|
|||||||
import com.cloud.projects.Project;
|
import com.cloud.projects.Project;
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
@APICommand(name = "associateIpAddress", description = "Acquires and associates a public IP to an account.", responseObject = IPAddressResponse.class, responseView = ResponseView.Restricted,
|
@APICommand(name = "associateIpAddress", description = "Acquires and associates a public IP to an account. Either of the parameters are required, i.e. either zoneId, or networkId, or vpcId ", responseObject = IPAddressResponse.class, responseView = ResponseView.Restricted,
|
||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||||
public class AssociateIPAddrCmd extends BaseAsyncCreateCmd {
|
public class AssociateIPAddrCmd extends BaseAsyncCreateCmd {
|
||||||
public static final Logger s_logger = Logger.getLogger(AssociateIPAddrCmd.class.getName());
|
public static final Logger s_logger = Logger.getLogger(AssociateIPAddrCmd.class.getName());
|
||||||
|
|||||||
@ -26,6 +26,8 @@ import java.util.Set;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.command.admin.internallb.ConfigureInternalLoadBalancerElementCmd;
|
import org.apache.cloudstack.api.command.admin.internallb.ConfigureInternalLoadBalancerElementCmd;
|
||||||
@ -81,6 +83,7 @@ import com.cloud.vm.ReservationContext;
|
|||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.VirtualMachineProfile;
|
import com.cloud.vm.VirtualMachineProfile;
|
||||||
import com.cloud.vm.dao.DomainRouterDao;
|
import com.cloud.vm.dao.DomainRouterDao;
|
||||||
|
import com.cloud.network.router.NetworkHelper;
|
||||||
|
|
||||||
public class InternalLoadBalancerElement extends AdapterBase implements LoadBalancingServiceProvider, InternalLoadBalancerElementService, IpDeployer {
|
public class InternalLoadBalancerElement extends AdapterBase implements LoadBalancingServiceProvider, InternalLoadBalancerElementService, IpDeployer {
|
||||||
private static final Logger s_logger = Logger.getLogger(InternalLoadBalancerElement.class);
|
private static final Logger s_logger = Logger.getLogger(InternalLoadBalancerElement.class);
|
||||||
@ -107,6 +110,9 @@ public class InternalLoadBalancerElement extends AdapterBase implements LoadBala
|
|||||||
ApplicationLoadBalancerRuleDao _appLbDao;
|
ApplicationLoadBalancerRuleDao _appLbDao;
|
||||||
@Inject
|
@Inject
|
||||||
EntityManager _entityMgr;
|
EntityManager _entityMgr;
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("networkHelper")
|
||||||
|
protected NetworkHelper _networkHelper;
|
||||||
|
|
||||||
protected InternalLoadBalancerElement() {
|
protected InternalLoadBalancerElement() {
|
||||||
}
|
}
|
||||||
@ -413,7 +419,7 @@ public class InternalLoadBalancerElement extends AdapterBase implements LoadBala
|
|||||||
if (routers == null || routers.isEmpty()) {
|
if (routers == null || routers.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return VirtualRouterElement.validateHAProxyLBRule(rule);
|
return _networkHelper.validateHAProxyLBRule(rule);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ import com.cloud.user.AccountManager;
|
|||||||
import com.cloud.utils.db.EntityManager;
|
import com.cloud.utils.db.EntityManager;
|
||||||
import com.cloud.utils.net.NetUtils;
|
import com.cloud.utils.net.NetUtils;
|
||||||
import com.cloud.vm.dao.DomainRouterDao;
|
import com.cloud.vm.dao.DomainRouterDao;
|
||||||
|
import com.cloud.network.router.NetworkHelper;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(basePackageClasses = {NetUtils.class},
|
@ComponentScan(basePackageClasses = {NetUtils.class},
|
||||||
@ -61,6 +62,11 @@ public class ElementChildTestConfiguration {
|
|||||||
return Mockito.mock(DomainRouterDao.class);
|
return Mockito.mock(DomainRouterDao.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public NetworkHelper networkHelper() {
|
||||||
|
return Mockito.mock(NetworkHelper.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public VirtualRouterProviderDao virtualRouterProviderDao() {
|
public VirtualRouterProviderDao virtualRouterProviderDao() {
|
||||||
return Mockito.mock(VirtualRouterProviderDao.class);
|
return Mockito.mock(VirtualRouterProviderDao.class);
|
||||||
|
|||||||
@ -23,7 +23,9 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import com.cloud.network.router.NetworkHelper;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.cloud.network.router.deployment.RouterDeploymentDefinition;
|
import org.cloud.network.router.deployment.RouterDeploymentDefinition;
|
||||||
@ -78,7 +80,6 @@ import com.cloud.network.dao.NetworkDetailsDao;
|
|||||||
import com.cloud.network.dao.OvsProviderDao;
|
import com.cloud.network.dao.OvsProviderDao;
|
||||||
import com.cloud.network.dao.VirtualRouterProviderDao;
|
import com.cloud.network.dao.VirtualRouterProviderDao;
|
||||||
import com.cloud.network.lb.LoadBalancingRule;
|
import com.cloud.network.lb.LoadBalancingRule;
|
||||||
import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy;
|
|
||||||
import com.cloud.network.lb.LoadBalancingRulesManager;
|
import com.cloud.network.lb.LoadBalancingRulesManager;
|
||||||
import com.cloud.network.router.VirtualRouter;
|
import com.cloud.network.router.VirtualRouter;
|
||||||
import com.cloud.network.router.VirtualRouter.Role;
|
import com.cloud.network.router.VirtualRouter.Role;
|
||||||
@ -95,13 +96,11 @@ import com.cloud.offerings.NetworkOfferingVO;
|
|||||||
import com.cloud.offerings.dao.NetworkOfferingDao;
|
import com.cloud.offerings.dao.NetworkOfferingDao;
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
import com.cloud.user.AccountManager;
|
import com.cloud.user.AccountManager;
|
||||||
import com.cloud.utils.Pair;
|
|
||||||
import com.cloud.utils.component.AdapterBase;
|
import com.cloud.utils.component.AdapterBase;
|
||||||
import com.cloud.utils.crypt.DBEncryptionUtil;
|
import com.cloud.utils.crypt.DBEncryptionUtil;
|
||||||
import com.cloud.utils.db.QueryBuilder;
|
import com.cloud.utils.db.QueryBuilder;
|
||||||
import com.cloud.utils.db.SearchCriteria.Op;
|
import com.cloud.utils.db.SearchCriteria.Op;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.utils.net.NetUtils;
|
|
||||||
import com.cloud.vm.DomainRouterVO;
|
import com.cloud.vm.DomainRouterVO;
|
||||||
import com.cloud.vm.NicProfile;
|
import com.cloud.vm.NicProfile;
|
||||||
import com.cloud.vm.ReservationContext;
|
import com.cloud.vm.ReservationContext;
|
||||||
@ -171,6 +170,10 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
|||||||
@Inject
|
@Inject
|
||||||
protected RouterDeploymentDefinitionBuilder routerDeploymentDefinitionBuilder;
|
protected RouterDeploymentDefinitionBuilder routerDeploymentDefinitionBuilder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("networkHelper")
|
||||||
|
protected NetworkHelper _networkHelper;
|
||||||
|
|
||||||
protected boolean canHandle(final Network network, final Service service) {
|
protected boolean canHandle(final Network network, final Service service) {
|
||||||
final Long physicalNetworkId = _networkMdl.getPhysicalNetworkId(network);
|
final Long physicalNetworkId = _networkMdl.getPhysicalNetworkId(network);
|
||||||
if (physicalNetworkId == null) {
|
if (physicalNetworkId == null) {
|
||||||
@ -302,104 +305,6 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This function detects numbers like 12 ,32h ,42m .. etc,. 1) plain number
|
|
||||||
* like 12 2) time or tablesize like 12h, 34m, 45k, 54m , here last
|
|
||||||
* character is non-digit but from known characters .
|
|
||||||
*/
|
|
||||||
private static boolean containsOnlyNumbers(final String str, final String endChar) {
|
|
||||||
if (str == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String number = str;
|
|
||||||
if (endChar != null) {
|
|
||||||
boolean matchedEndChar = false;
|
|
||||||
if (str.length() < 2) {
|
|
||||||
return false; // at least one numeric and one char. example:
|
|
||||||
}
|
|
||||||
// 3h
|
|
||||||
final char strEnd = str.toCharArray()[str.length() - 1];
|
|
||||||
for (final char c : endChar.toCharArray()) {
|
|
||||||
if (strEnd == c) {
|
|
||||||
number = str.substring(0, str.length() - 1);
|
|
||||||
matchedEndChar = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!matchedEndChar) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Integer.parseInt(number);
|
|
||||||
} catch (final NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean validateHAProxyLBRule(final LoadBalancingRule rule) {
|
|
||||||
final String timeEndChar = "dhms";
|
|
||||||
|
|
||||||
if (rule.getSourcePortStart() == NetUtils.HAPROXY_STATS_PORT) {
|
|
||||||
s_logger.debug("Can't create LB on port 8081, haproxy is listening for LB stats on this port");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final LbStickinessPolicy stickinessPolicy : rule.getStickinessPolicies()) {
|
|
||||||
final List<Pair<String, String>> paramsList = stickinessPolicy.getParams();
|
|
||||||
|
|
||||||
if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
|
||||||
|
|
||||||
} else if (StickinessMethodType.SourceBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
|
||||||
String tablesize = "200k"; // optional
|
|
||||||
String expire = "30m"; // optional
|
|
||||||
|
|
||||||
/* overwrite default values with the stick parameters */
|
|
||||||
for (final Pair<String, String> paramKV : paramsList) {
|
|
||||||
final String key = paramKV.first();
|
|
||||||
final String value = paramKV.second();
|
|
||||||
if ("tablesize".equalsIgnoreCase(key)) {
|
|
||||||
tablesize = value;
|
|
||||||
}
|
|
||||||
if ("expire".equalsIgnoreCase(key)) {
|
|
||||||
expire = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (expire != null && !containsOnlyNumbers(expire, timeEndChar)) {
|
|
||||||
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: expire is not in timeformat: " + expire);
|
|
||||||
}
|
|
||||||
if (tablesize != null && !containsOnlyNumbers(tablesize, "kmg")) {
|
|
||||||
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: tablesize is not in size format: " + tablesize);
|
|
||||||
|
|
||||||
}
|
|
||||||
} else if (StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
|
||||||
String length = null; // optional
|
|
||||||
String holdTime = null; // optional
|
|
||||||
|
|
||||||
for (final Pair<String, String> paramKV : paramsList) {
|
|
||||||
final String key = paramKV.first();
|
|
||||||
final String value = paramKV.second();
|
|
||||||
if ("length".equalsIgnoreCase(key)) {
|
|
||||||
length = value;
|
|
||||||
}
|
|
||||||
if ("holdtime".equalsIgnoreCase(key)) {
|
|
||||||
holdTime = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length != null && !containsOnlyNumbers(length, null)) {
|
|
||||||
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: length is not a number: " + length);
|
|
||||||
}
|
|
||||||
if (holdTime != null && !containsOnlyNumbers(holdTime, timeEndChar) && !containsOnlyNumbers(holdTime, null)) {
|
|
||||||
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: holdtime is not in timeformat: " + holdTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean validateLBRule(final Network network, final LoadBalancingRule rule) {
|
public boolean validateLBRule(final Network network, final LoadBalancingRule rule) {
|
||||||
final List<LoadBalancingRule> rules = new ArrayList<LoadBalancingRule>();
|
final List<LoadBalancingRule> rules = new ArrayList<LoadBalancingRule>();
|
||||||
@ -409,7 +314,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
|||||||
if (routers == null || routers.isEmpty()) {
|
if (routers == null || routers.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return validateHAProxyLBRule(rule);
|
return _networkHelper.validateHAProxyLBRule(rule);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.cloud.network.router.deployment.RouterDeploymentDefinition;
|
import org.cloud.network.router.deployment.RouterDeploymentDefinition;
|
||||||
|
import com.cloud.network.lb.LoadBalancingRule;
|
||||||
|
|
||||||
import com.cloud.agent.api.to.NicTO;
|
import com.cloud.agent.api.to.NicTO;
|
||||||
import com.cloud.agent.manager.Commands;
|
import com.cloud.agent.manager.Commands;
|
||||||
@ -89,4 +90,6 @@ public interface NetworkHelper {
|
|||||||
|
|
||||||
public abstract LinkedHashMap<Network, List<? extends NicProfile>> configureGuestNic(RouterDeploymentDefinition routerDeploymentDefinition)
|
public abstract LinkedHashMap<Network, List<? extends NicProfile>> configureGuestNic(RouterDeploymentDefinition routerDeploymentDefinition)
|
||||||
throws ConcurrentOperationException, InsufficientAddressCapacityException;
|
throws ConcurrentOperationException, InsufficientAddressCapacityException;
|
||||||
|
|
||||||
|
public boolean validateHAProxyLBRule(final LoadBalancingRule rule);
|
||||||
}
|
}
|
||||||
@ -27,6 +27,12 @@ import java.util.Map;
|
|||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.ejb.Local;
|
import javax.ejb.Local;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import com.cloud.configuration.Config;
|
||||||
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.network.lb.LoadBalancingRule;
|
||||||
|
import com.cloud.network.rules.LbStickinessMethod;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||||
@ -147,6 +153,8 @@ public class NetworkHelperImpl implements NetworkHelper {
|
|||||||
protected VirtualMachineManager _itMgr;
|
protected VirtualMachineManager _itMgr;
|
||||||
@Inject
|
@Inject
|
||||||
protected IpAddressManager _ipAddrMgr;
|
protected IpAddressManager _ipAddrMgr;
|
||||||
|
@Inject
|
||||||
|
ConfigurationDao _configDao;
|
||||||
|
|
||||||
protected final Map<HypervisorType, ConfigKey<String>> hypervisorsMap = new HashMap<>();
|
protected final Map<HypervisorType, ConfigKey<String>> hypervisorsMap = new HashMap<>();
|
||||||
|
|
||||||
@ -771,4 +779,103 @@ public class NetworkHelperImpl implements NetworkHelper {
|
|||||||
public static void setVMInstanceName(final String vmInstanceName) {
|
public static void setVMInstanceName(final String vmInstanceName) {
|
||||||
s_vmInstanceName = vmInstanceName;
|
s_vmInstanceName = vmInstanceName;
|
||||||
}
|
}
|
||||||
|
public boolean validateHAProxyLBRule(final LoadBalancingRule rule) {
|
||||||
|
final String timeEndChar = "dhms";
|
||||||
|
int haproxy_stats_port = Integer.parseInt(_configDao.getValue(Config.NetworkLBHaproxyStatsPort.key()));
|
||||||
|
if (rule.getSourcePortStart() == haproxy_stats_port) {
|
||||||
|
if (s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("Can't create LB on port "+ haproxy_stats_port +", haproxy is listening for LB stats on this port");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final LoadBalancingRule.LbStickinessPolicy stickinessPolicy : rule.getStickinessPolicies()) {
|
||||||
|
final List<Pair<String, String>> paramsList = stickinessPolicy.getParams();
|
||||||
|
|
||||||
|
if (LbStickinessMethod.StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
||||||
|
|
||||||
|
} else if (LbStickinessMethod.StickinessMethodType.SourceBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
||||||
|
String tablesize = "200k"; // optional
|
||||||
|
String expire = "30m"; // optional
|
||||||
|
|
||||||
|
/* overwrite default values with the stick parameters */
|
||||||
|
for (final Pair<String, String> paramKV : paramsList) {
|
||||||
|
final String key = paramKV.first();
|
||||||
|
final String value = paramKV.second();
|
||||||
|
if ("tablesize".equalsIgnoreCase(key)) {
|
||||||
|
tablesize = value;
|
||||||
|
}
|
||||||
|
if ("expire".equalsIgnoreCase(key)) {
|
||||||
|
expire = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (expire != null && !containsOnlyNumbers(expire, timeEndChar)) {
|
||||||
|
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: expire is not in timeformat: " + expire);
|
||||||
|
}
|
||||||
|
if (tablesize != null && !containsOnlyNumbers(tablesize, "kmg")) {
|
||||||
|
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: tablesize is not in size format: " + tablesize);
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (LbStickinessMethod.StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) {
|
||||||
|
String length = null; // optional
|
||||||
|
String holdTime = null; // optional
|
||||||
|
|
||||||
|
for (final Pair<String, String> paramKV : paramsList) {
|
||||||
|
final String key = paramKV.first();
|
||||||
|
final String value = paramKV.second();
|
||||||
|
if ("length".equalsIgnoreCase(key)) {
|
||||||
|
length = value;
|
||||||
|
}
|
||||||
|
if ("holdtime".equalsIgnoreCase(key)) {
|
||||||
|
holdTime = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length != null && !containsOnlyNumbers(length, null)) {
|
||||||
|
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: length is not a number: " + length);
|
||||||
|
}
|
||||||
|
if (holdTime != null && !containsOnlyNumbers(holdTime, timeEndChar) && !containsOnlyNumbers(holdTime, null)) {
|
||||||
|
throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: holdtime is not in timeformat: " + holdTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function detects numbers like 12 ,32h ,42m .. etc,. 1) plain number
|
||||||
|
* like 12 2) time or tablesize like 12h, 34m, 45k, 54m , here last
|
||||||
|
* character is non-digit but from known characters .
|
||||||
|
*/
|
||||||
|
private static boolean containsOnlyNumbers(final String str, final String endChar) {
|
||||||
|
if (str == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String number = str;
|
||||||
|
if (endChar != null) {
|
||||||
|
boolean matchedEndChar = false;
|
||||||
|
if (str.length() < 2) {
|
||||||
|
return false; // at least one numeric and one char. example:
|
||||||
|
}
|
||||||
|
// 3h
|
||||||
|
final char strEnd = str.toCharArray()[str.length() - 1];
|
||||||
|
for (final char c : endChar.toCharArray()) {
|
||||||
|
if (strEnd == c) {
|
||||||
|
number = str.substring(0, str.length() - 1);
|
||||||
|
matchedEndChar = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matchedEndChar) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Integer.parseInt(number);
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user