Add MySQL DataBase (#6968)

This commit is contained in:
Wuyunfan-BUPT 2021-09-23 07:13:47 -05:00 committed by GitHub
parent 76ac84344c
commit 3c0508d2da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 2544 additions and 0 deletions

View File

@ -0,0 +1,36 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.configuration;
import com.alibaba.nacos.auth.util.AuthPropertyUtil;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Judge whether to user ExternalStorage by condition.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class ConditionOnExternalStorage implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return !AuthPropertyUtil.isEmbeddedStorage();
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.constant;
/**
* PropertiesConstant.
*
* @author lixiaoshuang
*/
public class PropertiesConstant {
public static final String NOTIFY_CONNECT_TIMEOUT = "notifyConnectTimeout";
public static final String NOTIFY_SOCKET_TIMEOUT = "notifySocketTimeout";
public static final String IS_HEALTH_CHECK = "isHealthCheck";
public static final String MAX_HEALTH_CHECK_FAIL_COUNT = "maxHealthCheckFailCount";
public static final String MAX_CONTENT = "maxContent";
public static final String IS_MANAGE_CAPACITY = "isManageCapacity";
public static final String IS_CAPACITY_LIMIT_CHECK = "isCapacityLimitCheck";
public static final String DEFAULT_CLUSTER_QUOTA = "defaultClusterQuota";
public static final String DEFAULT_GROUP_QUOTA = "defaultGroupQuota";
public static final String DEFAULT_TENANT_QUOTA = "defaultTenantQuota";
public static final String DEFAULT_MAX_SIZE = "defaultMaxSize";
public static final String DEFAULT_MAX_AGGR_COUNT = "defaultMaxAggrCount";
public static final String DEFAULT_MAX_AGGR_SIZE = "defaultMaxAggrSize";
public static final String CORRECT_USAGE_DELAY = "correctUsageDelay";
public static final String INITIAL_EXPANSION_PERCENT = "initialExpansionPercent";
public static final String SPRING_DATASOURCE_PLATFORM = "spring.datasource.platform";
public static final String MYSQL = "mysql";
public static final String EMBEDDED_STORAGE = "embeddedStorage";
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist;
import com.alibaba.nacos.auth.configuration.ConditionOnExternalStorage;
import com.alibaba.nacos.auth.model.Page;
import com.alibaba.nacos.auth.model.PermissionInfo;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import com.alibaba.nacos.auth.persist.repository.externel.ExternalStoragePersistServiceImpl;
import com.alibaba.nacos.auth.util.LogUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Implemetation of ExternalPermissionPersistServiceImpl.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@Conditional(value = ConditionOnExternalStorage.class)
@Component
public class ExternalPermissionPersistServiceImpl implements PermissionPersistService {
public static final PermissionRowMapper PERMISSION_ROW_MAPPER = new PermissionRowMapper();
@Autowired
private ExternalStoragePersistServiceImpl persistService;
private JdbcTemplate jt;
@PostConstruct
protected void init() {
jt = persistService.getJdbcTemplate();
}
@Override
public Page<PermissionInfo> getPermissions(String role, int pageNo, int pageSize) {
PaginationHelper<PermissionInfo> helper = persistService.createPaginationHelper();
String sqlCountRows = "SELECT count(*) FROM permissions WHERE ";
String sqlFetchRows = "SELECT role,resource,action FROM permissions WHERE ";
String where = " role= ? ";
List<String> params = new ArrayList<>();
if (StringUtils.isNotBlank(role)) {
params = Collections.singletonList(role);
} else {
where = " 1=1 ";
}
try {
Page<PermissionInfo> pageInfo = helper
.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo,
pageSize, PERMISSION_ROW_MAPPER);
if (pageInfo == null) {
pageInfo = new Page<>();
pageInfo.setTotalCount(0);
pageInfo.setPageItems(new ArrayList<>());
}
return pageInfo;
} catch (CannotGetJdbcConnectionException e) {
LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
throw e;
}
}
public static final class PermissionRowMapper implements RowMapper<PermissionInfo> {
@Override
public PermissionInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
PermissionInfo info = new PermissionInfo();
info.setResource(rs.getString("resource"));
info.setAction(rs.getString("action"));
info.setRole(rs.getString("role"));
return info;
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist;
import com.alibaba.nacos.auth.configuration.ConditionOnExternalStorage;
import com.alibaba.nacos.auth.model.Page;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import com.alibaba.nacos.auth.persist.repository.externel.ExternalStoragePersistServiceImpl;
import com.alibaba.nacos.auth.roles.RoleInfo;
import com.alibaba.nacos.auth.util.LogUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Implemetation of ExternalRolePersistServiceImpl.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@Conditional(value = ConditionOnExternalStorage.class)
@Component
public class ExternalRolePersistServiceImpl implements RolePersistService {
public static final RoleInfoRowMapper ROLE_INFO_ROW_MAPPER = new RoleInfoRowMapper();
@Autowired
private ExternalStoragePersistServiceImpl persistService;
private JdbcTemplate jt;
@PostConstruct
protected void init() {
jt = persistService.getJdbcTemplate();
}
@Override
public Page<RoleInfo> getRolesByUserName(String username, int pageNo, int pageSize) {
PaginationHelper<RoleInfo> helper = persistService.createPaginationHelper();
String sqlCountRows = "SELECT count(*) FROM roles WHERE ";
String sqlFetchRows = "SELECT role,username FROM roles WHERE ";
String where = " username= ? ";
List<String> params = new ArrayList<>();
if (StringUtils.isNotBlank(username)) {
params = Collections.singletonList(username);
} else {
where = " 1=1 ";
}
try {
return helper.fetchPage(sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize,
ROLE_INFO_ROW_MAPPER);
} catch (CannotGetJdbcConnectionException e) {
LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
throw e;
}
}
private static final class RoleInfoRowMapper implements RowMapper<RoleInfo> {
@Override
public RoleInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
RoleInfo roleInfo = new RoleInfo();
roleInfo.setRole(rs.getString("role"));
roleInfo.setUsername(rs.getString("username"));
return roleInfo;
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist;
import com.alibaba.nacos.auth.configuration.ConditionOnExternalStorage;
import com.alibaba.nacos.auth.model.Page;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import com.alibaba.nacos.auth.persist.repository.externel.ExternalStoragePersistServiceImpl;
import com.alibaba.nacos.auth.users.User;
import com.alibaba.nacos.auth.util.LogUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* Implemetation of ExternalUserPersistServiceImpl.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@Conditional(value = ConditionOnExternalStorage.class)
@Component
public class ExternalUserPersistServiceImpl implements UserPersistService {
public static final RowMapper<User> USER_ROW_MAPPER = new UserRowMapper();
@Autowired
private ExternalStoragePersistServiceImpl persistService;
private JdbcTemplate jt;
@PostConstruct
protected void init() {
jt = persistService.getJdbcTemplate();
}
/**
* Execute create user operation.
*
* @param username username string value.
* @param password password string value.
*/
@Override
public void createUser(String username, String password) {
String sql = "INSERT INTO users (username, password, enabled) VALUES (?, ?, ?)";
try {
jt.update(sql, username, password, true);
} catch (CannotGetJdbcConnectionException e) {
LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
throw e;
}
}
/**
* Execute find user by username operation.
*
* @param username username string value.
* @return User model.
*/
@Override
public User findUserByUsername(String username) {
String sql = "SELECT username,password FROM users WHERE username=? ";
try {
return this.jt.queryForObject(sql, new Object[] {username}, USER_ROW_MAPPER);
} catch (CannotGetJdbcConnectionException e) {
LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
throw e;
} catch (EmptyResultDataAccessException e) {
return null;
} catch (Exception e) {
LogUtil.FATAL_LOG.error("[db-other-error]" + e.getMessage(), e);
throw new RuntimeException(e);
}
}
@Override
public Page<User> getUsers(int pageNo, int pageSize) {
PaginationHelper<User> helper = persistService.createPaginationHelper();
String sqlCountRows = "SELECT count(*) FROM users WHERE ";
String sqlFetchRows = "SELECT username,password FROM users WHERE ";
String where = " 1=1 ";
try {
Page<User> pageInfo = helper
.fetchPage(sqlCountRows + where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
pageSize, USER_ROW_MAPPER);
if (pageInfo == null) {
pageInfo = new Page<>();
pageInfo.setTotalCount(0);
pageInfo.setPageItems(new ArrayList<>());
}
return pageInfo;
} catch (CannotGetJdbcConnectionException e) {
LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
throw e;
}
}
public static final class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
return user;
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.env.Environment;
import java.util.concurrent.TimeUnit;
/**
* DataSource pool properties.
*
* <p>Nacos server use HikariCP as the datasource pool. So the basic pool properties will based on {@link
* HikariDataSource}.
*
* @author xiweng.yy
*/
public class DataSourcePoolProperties {
public static final long DEFAULT_CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(30L);
public static final long DEFAULT_VALIDATION_TIMEOUT = TimeUnit.SECONDS.toMillis(10L);
public static final int DEFAULT_MAX_POOL_SIZE = 20;
public static final int DEFAULT_MINIMUM_IDLE = 2;
private final HikariDataSource dataSource;
private DataSourcePoolProperties() {
dataSource = new HikariDataSource();
dataSource.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
dataSource.setValidationTimeout(DEFAULT_VALIDATION_TIMEOUT);
dataSource.setMaximumPoolSize(DEFAULT_MAX_POOL_SIZE);
dataSource.setMinimumIdle(DEFAULT_MINIMUM_IDLE);
}
/**
* Build new Hikari config.
*
* @return new hikari config
*/
public static DataSourcePoolProperties build(Environment environment) {
DataSourcePoolProperties result = new DataSourcePoolProperties();
Binder.get(environment).bind("db.pool.config", Bindable.ofInstance(result.getDataSource()));
return result;
}
public void setDriverClassName(final String driverClassName) {
dataSource.setDriverClassName(driverClassName);
}
public void setJdbcUrl(final String jdbcUrl) {
dataSource.setJdbcUrl(jdbcUrl);
}
public void setUsername(final String username) {
dataSource.setUsername(username);
}
public void setPassword(final String password) {
dataSource.setPassword(password);
}
public HikariDataSource getDataSource() {
return dataSource;
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.support.TransactionTemplate;
import java.io.IOException;
/**
* Datasource interface.
*
* @author Nacos
*/
public interface DataSourceService {
/**
* Initialize the relevant resource information.
*
* @throws Exception exception.
*/
void init() throws Exception;
/**
* Reload.
*
* @throws IOException exception.
*/
void reload() throws IOException;
/**
* Check master db.
*
* @return is master.
*/
boolean checkMasterWritable();
/**
* Get jdbc template.
*
* @return JdbcTemplate.
*/
JdbcTemplate getJdbcTemplate();
/**
* Get transaction template.
*
* @return TransactionTemplate.
*/
TransactionTemplate getTransactionTemplate();
/**
* Get current db url.
*
* @return database url
*/
String getCurrentDbUrl();
/**
* Get heath information.
*
* @return heath info.
*/
String getHealth();
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import com.alibaba.nacos.auth.util.AuthPropertyUtil;
import org.springframework.stereotype.Component;
/**
* Datasource adapter.
*
* @author Nacos
*/
@Component
public class DynamicDataSource {
private DataSourceService localDataSourceService = null;
private DataSourceService basicDataSourceService = null;
private static final DynamicDataSource INSTANCE = new DynamicDataSource();
public static DynamicDataSource getInstance() {
return INSTANCE;
}
public synchronized DataSourceService getDataSource() {
try {
// Embedded storage is used by default in stand-alone mode
// In cluster mode, external databases are used by default
if (AuthPropertyUtil.isEmbeddedStorage()) {
if (localDataSourceService == null) {
localDataSourceService = new LocalDataSourceServiceImpl();
localDataSourceService.init();
}
return localDataSourceService;
} else {
if (basicDataSourceService == null) {
basicDataSourceService = new ExternalDataSourceServiceImpl();
basicDataSourceService.init();
}
return basicDataSourceService;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import com.alibaba.nacos.auth.util.Preconditions;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.env.Environment;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static com.alibaba.nacos.common.utils.CollectionUtils.getOrDefault;
/**
* Properties of external DataSource.
*
* @author Nacos
*/
public class ExternalDataSourceProperties {
private static final String JDBC_DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
private static final String TEST_QUERY = "SELECT 1";
private Integer num;
private List<String> url = new ArrayList<>();
private List<String> user = new ArrayList<>();
private List<String> password = new ArrayList<>();
public void setNum(Integer num) {
this.num = num;
}
public void setUrl(List<String> url) {
this.url = url;
}
public void setUser(List<String> user) {
this.user = user;
}
public void setPassword(List<String> password) {
this.password = password;
}
/**
* Build serveral HikariDataSource.
*
* @param environment {@link Environment}
* @param callback Callback function when constructing data source
* @return List of {@link HikariDataSource}
*/
List<HikariDataSource> build(Environment environment, Callback<HikariDataSource> callback) {
List<HikariDataSource> dataSources = new ArrayList<>();
Binder.get(environment).bind("db", Bindable.ofInstance(this));
Preconditions.checkArgument(Objects.nonNull(num), "db.num is null");
Preconditions.checkArgument(CollectionUtils.isNotEmpty(user), "db.user or db.user.[index] is null");
Preconditions.checkArgument(CollectionUtils.isNotEmpty(password), "db.password or db.password.[index] is null");
for (int index = 0; index < num; index++) {
int currentSize = index + 1;
Preconditions.checkArgument(url.size() >= currentSize, "db.url.%s is null", index);
DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(environment);
poolProperties.setDriverClassName(JDBC_DRIVER_NAME);
poolProperties.setJdbcUrl(url.get(index).trim());
poolProperties.setUsername(getOrDefault(user, index, user.get(0)).trim());
poolProperties.setPassword(getOrDefault(password, index, password.get(0)).trim());
HikariDataSource ds = poolProperties.getDataSource();
ds.setConnectionTestQuery(TEST_QUERY);
ds.setIdleTimeout(TimeUnit.MINUTES.toMillis(10L));
ds.setConnectionTimeout(TimeUnit.SECONDS.toMillis(3L));
dataSources.add(ds);
callback.accept(ds);
}
Preconditions.checkArgument(CollectionUtils.isNotEmpty(dataSources), "no datasource available");
return dataSources;
}
interface Callback<D> {
/**
* Perform custom logic.
*
* @param datasource dataSource.
*/
void accept(D datasource);
}
}

View File

@ -0,0 +1,260 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import com.alibaba.nacos.auth.util.AuthPropertyUtil;
import com.alibaba.nacos.common.utils.ConvertUtils;
import com.alibaba.nacos.common.utils.InternetAddressUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.alibaba.nacos.auth.util.LogUtil.DEFAULT_LOG;
import static com.alibaba.nacos.auth.util.LogUtil.FATAL_LOG;
/**
* Base data source.
*
* @author Nacos
*/
public class ExternalDataSourceServiceImpl implements DataSourceService {
/**
* JDBC execute timeout value, unit:second.
*/
private int queryTimeout = 3;
private static final int TRANSACTION_QUERY_TIMEOUT = 5;
private static final int DB_MASTER_SELECT_THRESHOLD = 1;
private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error";
private List<HikariDataSource> dataSourceList = new ArrayList<>();
private JdbcTemplate jt;
private DataSourceTransactionManager tm;
private TransactionTemplate tjt;
private JdbcTemplate testMasterJT;
private JdbcTemplate testMasterWritableJT;
private volatile List<JdbcTemplate> testJtList;
private volatile List<Boolean> isHealthList;
private volatile int masterIndex;
@Override
public void init() {
queryTimeout = ConvertUtils.toInt(System.getProperty("QUERYTIMEOUT"), 3);
jt = new JdbcTemplate();
// Set the maximum number of records to prevent memory expansion
jt.setMaxRows(50000);
jt.setQueryTimeout(queryTimeout);
testMasterJT = new JdbcTemplate();
testMasterJT.setQueryTimeout(queryTimeout);
testMasterWritableJT = new JdbcTemplate();
// Prevent the login interface from being too long because the main library is not available
testMasterWritableJT.setQueryTimeout(1);
// Database health check
testJtList = new ArrayList<JdbcTemplate>();
isHealthList = new ArrayList<Boolean>();
tm = new DataSourceTransactionManager();
tjt = new TransactionTemplate(tm);
// Transaction timeout needs to be distinguished from ordinary operations.
tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT);
if (AuthPropertyUtil.isUseExternalDB()) {
try {
reload();
} catch (IOException e) {
FATAL_LOG.error("[ExternalDataSourceService] dats source reload error", e);
throw new RuntimeException(DB_LOAD_ERROR_MSG);
}
/*
if (this.dataSourceList.size() > DB_MASTER_SELECT_THRESHOLD) {
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
}
ConfigExecutor.scheduleConfigTask(new CheckDbHealthTask(), 10, 10, TimeUnit.SECONDS);
*/
}
}
@Override
public synchronized void reload() throws IOException {
try {
dataSourceList = new ExternalDataSourceProperties()
.build(EnvUtil.getEnvironment(), (dataSource) -> {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setQueryTimeout(queryTimeout);
jdbcTemplate.setDataSource(dataSource);
testJtList.add(jdbcTemplate);
isHealthList.add(Boolean.TRUE);
});
new SelectMasterTask().run();
//new CheckDbHealthTask().run();
} catch (RuntimeException e) {
FATAL_LOG.error(DB_LOAD_ERROR_MSG, e);
throw new IOException(e);
}
}
@Override
public boolean checkMasterWritable() {
testMasterWritableJT.setDataSource(jt.getDataSource());
// Prevent the login interface from being too long because the main library is not available
testMasterWritableJT.setQueryTimeout(1);
String sql = " SELECT @@read_only ";
try {
Integer result = testMasterWritableJT.queryForObject(sql, Integer.class);
if (result == null) {
return false;
} else {
return result == 0;
}
} catch (CannotGetJdbcConnectionException e) {
FATAL_LOG.error("[db-error] " + e.toString(), e);
return false;
}
}
@Override
public JdbcTemplate getJdbcTemplate() {
return this.jt;
}
@Override
public TransactionTemplate getTransactionTemplate() {
return this.tjt;
}
@Override
public String getCurrentDbUrl() {
DataSource ds = this.jt.getDataSource();
if (ds == null) {
return StringUtils.EMPTY;
}
HikariDataSource bds = (HikariDataSource) ds;
return bds.getJdbcUrl();
}
@Override
public String getHealth() {
for (int i = 0; i < isHealthList.size(); i++) {
if (!isHealthList.get(i)) {
if (i == masterIndex) {
// The master is unhealthy.
return "DOWN:" + InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl());
} else {
// The slave is unhealthy.
return "WARN:" + InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl());
}
}
}
return "UP";
}
class SelectMasterTask implements Runnable {
@Override
public void run() {
if (DEFAULT_LOG.isDebugEnabled()) {
DEFAULT_LOG.debug("check master db.");
}
boolean isFound = false;
int index = -1;
for (HikariDataSource ds : dataSourceList) {
index++;
testMasterJT.setDataSource(ds);
testMasterJT.setQueryTimeout(queryTimeout);
try {
testMasterJT.update("DELETE FROM config_info WHERE data_id='com.alibaba.nacos.testMasterDB'");
if (jt.getDataSource() != ds) {
FATAL_LOG.warn("[master-db] {}", ds.getJdbcUrl());
}
jt.setDataSource(ds);
tm.setDataSource(ds);
isFound = true;
masterIndex = index;
break;
} catch (DataAccessException e) { // read only
FATAL_LOG.warn("[master-db] master db access error", e);
}
}
if (!isFound) {
FATAL_LOG.error("[master-db] master db not found.");
//MetricsMonitor.getDbException().increment();
}
}
}
/*
@SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
class CheckDbHealthTask implements Runnable {
@Override
public void run() {
if (DEFAULT_LOG.isDebugEnabled()) {
DEFAULT_LOG.debug("check db health.");
}
String sql = "SELECT * FROM config_info_beta WHERE id = 1";
for (int i = 0; i < testJtList.size(); i++) {
JdbcTemplate jdbcTemplate = testJtList.get(i);
try {
jdbcTemplate.query(sql, CONFIG_INFO4BETA_ROW_MAPPER);
isHealthList.set(i, Boolean.TRUE);
} catch (DataAccessException e) {
if (i == masterIndex) {
FATAL_LOG.error("[db-error] master db {} down.",
InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl()));
} else {
FATAL_LOG.error("[db-error] slave db {} down.",
InternetAddressUtil.getIPFromString(dataSourceList.get(i).getJdbcUrl()));
}
isHealthList.set(i, Boolean.FALSE);
MetricsMonitor.getDbException().increment();
}
}
}
}*/
}

View File

@ -0,0 +1,265 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.auth.util.AuthPropertyUtil;
import com.alibaba.nacos.auth.util.LogUtil;
import com.alibaba.nacos.common.utils.IoUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.DiskUtils;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
/**
* local data source.
*
* @author Nacos
*/
public class LocalDataSourceServiceImpl implements DataSourceService {
private final String jdbcDriverName = "org.apache.derby.jdbc.EmbeddedDriver";
private final String userName = "nacos";
private final String password = "nacos";
private final String derbyBaseDir = "data" + File.separator + "derby-data";
private final String derbyShutdownErrMsg = "Derby system shutdown.";
private volatile JdbcTemplate jt;
private volatile TransactionTemplate tjt;
private boolean initialize = false;
private boolean jdbcTemplateInit = false;
private String healthStatus = "UP";
private static final String ENCODE = "UTF-8";
@PostConstruct
@Override
public synchronized void init() throws Exception {
if (AuthPropertyUtil.isUseExternalDB()) {
return;
}
if (!initialize) {
LogUtil.DEFAULT_LOG.info("use local db service for init");
final String jdbcUrl =
"jdbc:derby:" + Paths.get(EnvUtil.getNacosHome(), derbyBaseDir).toString()
+ ";create=true";
initialize(jdbcUrl);
initialize = true;
}
}
@Override
public synchronized void reload() {
DataSource ds = jt.getDataSource();
if (ds == null) {
throw new RuntimeException("datasource is null");
}
try {
execute(ds.getConnection(), "META-INF/schema.sql");
} catch (Exception e) {
if (LogUtil.DEFAULT_LOG.isErrorEnabled()) {
LogUtil.DEFAULT_LOG.error(e.getMessage(), e);
}
throw new NacosRuntimeException(NacosException.SERVER_ERROR, "load schema.sql error.", e);
}
}
public DataSource getDatasource() {
return jt.getDataSource();
}
/**
* Clean and reopen Derby.
*
* @throws Exception exception.
*/
public void cleanAndReopenDerby() throws Exception {
doDerbyClean();
final String jdbcUrl =
"jdbc:derby:" + Paths.get(EnvUtil.getNacosHome(), derbyBaseDir).toString() + ";create=true";
initialize(jdbcUrl);
}
/**
* Restore derby.
*
* @param jdbcUrl jdbcUrl string value.
* @param callable callable.
* @throws Exception exception.
*/
public void restoreDerby(String jdbcUrl, Callable<Void> callable) throws Exception {
doDerbyClean();
callable.call();
initialize(jdbcUrl);
}
private void doDerbyClean() throws Exception {
LogUtil.DEFAULT_LOG.warn("use local db service for reopenDerby");
try {
DriverManager.getConnection("jdbc:derby:;shutdown=true");
} catch (Exception e) {
// An error is thrown when the Derby shutdown is executed, which should be ignored
if (!StringUtils.containsIgnoreCase(e.getMessage(), derbyShutdownErrMsg)) {
throw e;
}
}
DiskUtils.deleteDirectory(Paths.get(EnvUtil.getNacosHome(), derbyBaseDir).toString());
}
private synchronized void initialize(String jdbcUrl) {
DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(EnvUtil.getEnvironment());
poolProperties.setDriverClassName(jdbcDriverName);
poolProperties.setJdbcUrl(jdbcUrl);
poolProperties.setUsername(userName);
poolProperties.setPassword(password);
HikariDataSource ds = poolProperties.getDataSource();
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(ds);
if (jdbcTemplateInit) {
jt.setDataSource(ds);
tjt.setTransactionManager(tm);
} else {
jt = new JdbcTemplate();
jt.setMaxRows(50000);
jt.setQueryTimeout(5000);
jt.setDataSource(ds);
tjt = new TransactionTemplate(tm);
tjt.setTimeout(5000);
jdbcTemplateInit = true;
}
reload();
}
@Override
public boolean checkMasterWritable() {
return true;
}
@Override
public JdbcTemplate getJdbcTemplate() {
return jt;
}
@Override
public TransactionTemplate getTransactionTemplate() {
return tjt;
}
@Override
public String getCurrentDbUrl() {
return "jdbc:derby:" + EnvUtil.getNacosHome() + File.separator + derbyBaseDir + ";create=true";
}
@Override
public String getHealth() {
return healthStatus;
}
public void setHealthStatus(String healthStatus) {
this.healthStatus = healthStatus;
}
/**
* Load sql.
*
* @param sqlFile sql.
* @return sqls.
* @throws Exception Exception.
*/
private List<String> loadSql(String sqlFile) throws Exception {
List<String> sqlList = new ArrayList<String>();
InputStream sqlFileIn = null;
try {
File file = new File(
EnvUtil.getNacosHome() + File.separator + "conf" + File.separator + "schema.sql");
if (StringUtils.isBlank(EnvUtil.getNacosHome()) || !file.exists()) {
ClassLoader classLoader = getClass().getClassLoader();
URL url = classLoader.getResource(sqlFile);
sqlFileIn = url.openStream();
} else {
sqlFileIn = new FileInputStream(file);
}
StringBuilder sqlSb = new StringBuilder();
byte[] buff = new byte[1024];
int byteRead = 0;
while ((byteRead = sqlFileIn.read(buff)) != -1) {
sqlSb.append(new String(buff, 0, byteRead, ENCODE));
}
String[] sqlArr = sqlSb.toString().split(";");
for (int i = 0; i < sqlArr.length; i++) {
String sql = sqlArr[i].replaceAll("--.*", "").trim();
if (StringUtils.isNotEmpty(sql)) {
sqlList.add(sql);
}
}
return sqlList;
} catch (Exception ex) {
throw new Exception(ex.getMessage());
} finally {
IoUtils.closeQuietly(sqlFileIn);
}
}
/**
* Execute sql.
*
* @param conn connect.
* @param sqlFile sql.
* @throws Exception Exception.
*/
private void execute(Connection conn, String sqlFile) throws Exception {
try (Statement stmt = conn.createStatement()) {
List<String> sqlList = loadSql(sqlFile);
for (String sql : sqlList) {
try {
stmt.execute(sql);
} catch (Exception e) {
LogUtil.DEFAULT_LOG.warn(e.getMessage());
}
}
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.repository;
import com.alibaba.nacos.auth.model.Page;
import org.springframework.jdbc.core.RowMapper;
/**
* Pagination Utils interface.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@SuppressWarnings("PMD.AbstractMethodOrInterfaceMethodMustUseJavadocRule")
public interface PaginationHelper<E> {
Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args, final int pageNo,
final int pageSize, final RowMapper<E> rowMapper);
Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args, final int pageNo,
final int pageSize, final Long lastMaxId, final RowMapper<E> rowMapper);
Page<E> fetchPageLimit(final String sqlCountRows, final String sqlFetchRows, final Object[] args, final int pageNo,
final int pageSize, final RowMapper<E> rowMapper);
Page<E> fetchPageLimit(final String sqlCountRows, final Object[] args1, final String sqlFetchRows,
final Object[] args2, final int pageNo, final int pageSize, final RowMapper<E> rowMapper);
Page<E> fetchPageLimit(final String sqlFetchRows, final Object[] args, final int pageNo, final int pageSize,
final RowMapper<E> rowMapper);
}

View File

@ -0,0 +1,211 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.repository.externel;
import com.alibaba.nacos.auth.model.Page;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import com.alibaba.nacos.auth.util.AuthPropertyUtil;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
/**
* External Storage Pagination utils.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
class ExternalStoragePaginationHelperImpl<E> implements PaginationHelper {
private final JdbcTemplate jdbcTemplate;
public ExternalStoragePaginationHelperImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* Take paging.
*
* @param sqlCountRows query total SQL
* @param sqlFetchRows query data sql
* @param args query parameters
* @param pageNo page number
* @param pageSize page size
* @param rowMapper {@link RowMapper}
* @return Paginated data {@code <E>}
*/
@Override
public Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args,
final int pageNo, final int pageSize, final RowMapper rowMapper) {
return fetchPage(sqlCountRows, sqlFetchRows, args, pageNo, pageSize, null, rowMapper);
}
@Override
public Page<E> fetchPage(final String sqlCountRows, final String sqlFetchRows, final Object[] args,
final int pageNo, final int pageSize, final Long lastMaxId, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records.
Integer rowCountInt = jdbcTemplate.queryForObject(sqlCountRows, args, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Compute pages count
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<E>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
final int startRow = (pageNo - 1) * pageSize;
String selectSql = "";
if (isDerby()) {
selectSql = sqlFetchRows + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY";
} else if (lastMaxId != null) {
selectSql = sqlFetchRows + " AND id > " + lastMaxId + " ORDER BY id ASC" + " LIMIT " + 0 + "," + pageSize;
} else {
selectSql = sqlFetchRows + " LIMIT " + startRow + "," + pageSize;
}
List<E> result = jdbcTemplate.query(selectSql, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlCountRows, final String sqlFetchRows, final Object[] args,
final int pageNo, final int pageSize, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records
Integer rowCountInt = jdbcTemplate.queryForObject(sqlCountRows, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Compute pages count
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<E>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
String selectSql = sqlFetchRows;
if (isDerby()) {
selectSql = selectSql.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");
}
List<E> result = jdbcTemplate.query(selectSql, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlCountRows, final Object[] args1, final String sqlFetchRows,
final Object[] args2, final int pageNo, final int pageSize, final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Query the total number of current records
Integer rowCountInt = jdbcTemplate.queryForObject(sqlCountRows, args1, Integer.class);
if (rowCountInt == null) {
throw new IllegalArgumentException("fetchPageLimit error");
}
// Compute pages count
int pageCount = rowCountInt / pageSize;
if (rowCountInt > pageSize * pageCount) {
pageCount++;
}
// Create Page object
final Page<E> page = new Page<E>();
page.setPageNumber(pageNo);
page.setPagesAvailable(pageCount);
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return page;
}
String selectSql = sqlFetchRows;
if (isDerby()) {
selectSql = selectSql.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");
}
List<E> result = jdbcTemplate.query(selectSql, args2, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
@Override
public Page<E> fetchPageLimit(final String sqlFetchRows, final Object[] args, final int pageNo, final int pageSize,
final RowMapper rowMapper) {
if (pageNo <= 0 || pageSize <= 0) {
throw new IllegalArgumentException("pageNo and pageSize must be greater than zero");
}
// Create Page object
final Page<E> page = new Page<E>();
String selectSql = sqlFetchRows;
if (isDerby()) {
selectSql = selectSql.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");
}
List<E> result = jdbcTemplate.query(selectSql, args, rowMapper);
for (E item : result) {
page.getPageItems().add(item);
}
return page;
}
private boolean isDerby() {
return (EnvUtil.getStandaloneMode() && !AuthPropertyUtil.isUseExternalDB()) || AuthPropertyUtil
.isEmbeddedStorage();
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.repository.externel;
import com.alibaba.nacos.auth.configuration.ConditionOnExternalStorage;
import com.alibaba.nacos.auth.persist.datasource.DataSourceService;
import com.alibaba.nacos.auth.persist.datasource.DynamicDataSource;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import org.springframework.context.annotation.Conditional;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@SuppressWarnings(value = {"PMD.MethodReturnWrapperTypeRule", "checkstyle:linelength"})
@Conditional(value = ConditionOnExternalStorage.class)
@Component
public class ExternalStoragePersistServiceImpl {
private DataSourceService dataSourceService;
protected JdbcTemplate jt;
/**
* init datasource.
*/
@PostConstruct
public void init() {
dataSourceService = DynamicDataSource.getInstance().getDataSource();
jt = getJdbcTemplate();
}
/**
* For unit testing.
*/
public JdbcTemplate getJdbcTemplate() {
return this.dataSourceService.getJdbcTemplate();
}
public <E> PaginationHelper<E> createPaginationHelper() {
return new ExternalStoragePaginationHelperImpl<E>(jt);
}
}

View File

@ -0,0 +1,338 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.util;
import com.alibaba.nacos.auth.constant.PropertiesConstant;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Properties util.
*
* @author Nacos
*/
public class AuthPropertyUtil implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LogUtil.DEFAULT_LOG;
private static int notifyConnectTimeout = 100;
private static int notifySocketTimeout = 200;
private static int maxHealthCheckFailCount = 12;
private static boolean isHealthCheck = true;
private static int maxContent = 10 * 1024 * 1024;
/**
* Whether to enable capacity management.
*/
private static boolean isManageCapacity = true;
/**
* Whether to enable the limit check function of capacity management, including the upper limit of configuration
* number, configuration content size limit, etc.
*/
private static boolean isCapacityLimitCheck = false;
/**
* The default cluster capacity limit.
*/
private static int defaultClusterQuota = 100000;
/**
* the default capacity limit per Group.
*/
private static int defaultGroupQuota = 200;
/**
* The default capacity limit per Tenant.
*/
private static int defaultTenantQuota = 200;
/**
* The maximum size of the content in the configuration of a single, unit for bytes.
*/
private static int defaultMaxSize = 100 * 1024;
/**
* The default Maximum number of aggregated data.
*/
private static int defaultMaxAggrCount = 10000;
/**
* The maximum size of content in a single subconfiguration of aggregated data.
*/
private static int defaultMaxAggrSize = 1024;
/**
* Initialize the expansion percentage of capacity has reached the limit.
*/
private static int initialExpansionPercent = 100;
/**
* Fixed capacity information table usage (usage) time interval, the unit is in seconds.
*/
private static int correctUsageDelay = 10 * 60;
/**
* Standalone mode uses DB.
*/
private static boolean useExternalDB = false;
/**
* Inline storage value = ${nacos.standalone}.
*/
private static boolean embeddedStorage = EnvUtil.getStandaloneMode();
public static int getNotifyConnectTimeout() {
return notifyConnectTimeout;
}
public static void setNotifyConnectTimeout(int notifyConnectTimeout) {
AuthPropertyUtil.notifyConnectTimeout = notifyConnectTimeout;
}
public static int getNotifySocketTimeout() {
return notifySocketTimeout;
}
public static void setNotifySocketTimeout(int notifySocketTimeout) {
AuthPropertyUtil.notifySocketTimeout = notifySocketTimeout;
}
public static int getMaxHealthCheckFailCount() {
return maxHealthCheckFailCount;
}
public static void setMaxHealthCheckFailCount(int maxHealthCheckFailCount) {
AuthPropertyUtil.maxHealthCheckFailCount = maxHealthCheckFailCount;
}
public static boolean isHealthCheck() {
return isHealthCheck;
}
public static void setHealthCheck(boolean isHealthCheck) {
AuthPropertyUtil.isHealthCheck = isHealthCheck;
}
public static int getMaxContent() {
return maxContent;
}
public static void setMaxContent(int maxContent) {
AuthPropertyUtil.maxContent = maxContent;
}
public static boolean isManageCapacity() {
return isManageCapacity;
}
public static void setManageCapacity(boolean isManageCapacity) {
AuthPropertyUtil.isManageCapacity = isManageCapacity;
}
public static int getDefaultClusterQuota() {
return defaultClusterQuota;
}
public static void setDefaultClusterQuota(int defaultClusterQuota) {
AuthPropertyUtil.defaultClusterQuota = defaultClusterQuota;
}
public static boolean isCapacityLimitCheck() {
return isCapacityLimitCheck;
}
public static void setCapacityLimitCheck(boolean isCapacityLimitCheck) {
AuthPropertyUtil.isCapacityLimitCheck = isCapacityLimitCheck;
}
public static int getDefaultGroupQuota() {
return defaultGroupQuota;
}
public static void setDefaultGroupQuota(int defaultGroupQuota) {
AuthPropertyUtil.defaultGroupQuota = defaultGroupQuota;
}
public static int getDefaultTenantQuota() {
return defaultTenantQuota;
}
public static void setDefaultTenantQuota(int defaultTenantQuota) {
AuthPropertyUtil.defaultTenantQuota = defaultTenantQuota;
}
public static int getInitialExpansionPercent() {
return initialExpansionPercent;
}
public static void setInitialExpansionPercent(int initialExpansionPercent) {
AuthPropertyUtil.initialExpansionPercent = initialExpansionPercent;
}
public static int getDefaultMaxSize() {
return defaultMaxSize;
}
public static void setDefaultMaxSize(int defaultMaxSize) {
AuthPropertyUtil.defaultMaxSize = defaultMaxSize;
}
public static int getDefaultMaxAggrCount() {
return defaultMaxAggrCount;
}
public static void setDefaultMaxAggrCount(int defaultMaxAggrCount) {
AuthPropertyUtil.defaultMaxAggrCount = defaultMaxAggrCount;
}
public static int getDefaultMaxAggrSize() {
return defaultMaxAggrSize;
}
public static void setDefaultMaxAggrSize(int defaultMaxAggrSize) {
AuthPropertyUtil.defaultMaxAggrSize = defaultMaxAggrSize;
}
public static int getCorrectUsageDelay() {
return correctUsageDelay;
}
public static void setCorrectUsageDelay(int correctUsageDelay) {
AuthPropertyUtil.correctUsageDelay = correctUsageDelay;
}
public static boolean isStandaloneMode() {
return EnvUtil.getStandaloneMode();
}
public static boolean isUseExternalDB() {
return useExternalDB;
}
public static void setUseExternalDB(boolean useExternalDB) {
AuthPropertyUtil.useExternalDB = useExternalDB;
}
public static boolean isEmbeddedStorage() {
return embeddedStorage;
}
// Determines whether to read the data directly
// if use mysql, Reduce database read pressure
// if use raft+derby, Reduce leader read pressure
public static boolean isDirectRead() {
return EnvUtil.getStandaloneMode() && isEmbeddedStorage();
}
public static void setEmbeddedStorage(boolean embeddedStorage) {
AuthPropertyUtil.embeddedStorage = embeddedStorage;
}
private void loadSetting() {
try {
setNotifyConnectTimeout(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.NOTIFY_CONNECT_TIMEOUT,
String.valueOf(notifyConnectTimeout))));
LOGGER.info("notifyConnectTimeout:{}", notifyConnectTimeout);
setNotifySocketTimeout(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.NOTIFY_SOCKET_TIMEOUT,
String.valueOf(notifySocketTimeout))));
LOGGER.info("notifySocketTimeout:{}", notifySocketTimeout);
setHealthCheck(Boolean.parseBoolean(
EnvUtil.getProperty(PropertiesConstant.IS_HEALTH_CHECK, String.valueOf(isHealthCheck))));
LOGGER.info("isHealthCheck:{}", isHealthCheck);
setMaxHealthCheckFailCount(Integer.parseInt(
EnvUtil.getProperty(PropertiesConstant.MAX_HEALTH_CHECK_FAIL_COUNT,
String.valueOf(maxHealthCheckFailCount))));
LOGGER.info("maxHealthCheckFailCount:{}", maxHealthCheckFailCount);
setMaxContent(
Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.MAX_CONTENT, String.valueOf(maxContent))));
LOGGER.info("maxContent:{}", maxContent);
// capacity management
setManageCapacity(getBoolean(PropertiesConstant.IS_MANAGE_CAPACITY, isManageCapacity));
setCapacityLimitCheck(getBoolean(PropertiesConstant.IS_CAPACITY_LIMIT_CHECK, isCapacityLimitCheck));
setDefaultClusterQuota(getInt(PropertiesConstant.DEFAULT_CLUSTER_QUOTA, defaultClusterQuota));
setDefaultGroupQuota(getInt(PropertiesConstant.DEFAULT_GROUP_QUOTA, defaultGroupQuota));
setDefaultTenantQuota(getInt(PropertiesConstant.DEFAULT_TENANT_QUOTA, defaultTenantQuota));
setDefaultMaxSize(getInt(PropertiesConstant.DEFAULT_MAX_SIZE, defaultMaxSize));
setDefaultMaxAggrCount(getInt(PropertiesConstant.DEFAULT_MAX_AGGR_COUNT, defaultMaxAggrCount));
setDefaultMaxAggrSize(getInt(PropertiesConstant.DEFAULT_MAX_AGGR_SIZE, defaultMaxAggrSize));
setCorrectUsageDelay(getInt(PropertiesConstant.CORRECT_USAGE_DELAY, correctUsageDelay));
setInitialExpansionPercent(getInt(PropertiesConstant.INITIAL_EXPANSION_PERCENT, initialExpansionPercent));
// External data sources are used by default in cluster mode
setUseExternalDB(PropertiesConstant.MYSQL
.equalsIgnoreCase(getString(PropertiesConstant.SPRING_DATASOURCE_PLATFORM, "")));
// must initialize after setUseExternalDB
// This value is true in stand-alone mode and false in cluster mode
// If this value is set to true in cluster mode, nacos's distributed storage engine is turned on
// default value is depend on ${nacos.standalone}
if (isUseExternalDB()) {
setEmbeddedStorage(false);
} else {
boolean embeddedStorage =
AuthPropertyUtil.embeddedStorage || Boolean.getBoolean(PropertiesConstant.EMBEDDED_STORAGE);
setEmbeddedStorage(embeddedStorage);
// If the embedded data source storage is not turned on, it is automatically
// upgraded to the external data source storage, as before
if (!embeddedStorage) {
setUseExternalDB(true);
}
}
} catch (Exception e) {
LOGGER.error("read application.properties failed", e);
throw e;
}
}
private boolean getBoolean(String key, boolean defaultValue) {
return Boolean.parseBoolean(getString(key, String.valueOf(defaultValue)));
}
private int getInt(String key, int defaultValue) {
return Integer.parseInt(getString(key, String.valueOf(defaultValue)));
}
private String getString(String key, String defaultValue) {
String value = getProperty(key);
if (value == null) {
return defaultValue;
}
LOGGER.info("{}:{}", key, value);
return value;
}
public String getProperty(String key) {
return EnvUtil.getProperty(key);
}
public String getProperty(String key, String defaultValue) {
return EnvUtil.getProperty(key, defaultValue);
}
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
loadSetting();
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.util;
import ch.qos.logback.classic.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Log util.
*
* @author Nacos
*/
public class LogUtil {
/**
* Default log.
*/
public static final Logger DEFAULT_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.startLog");
/**
* Fatal error log, require alarm.
*/
public static final Logger FATAL_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.fatal");
/**
* Http client log.
*/
public static final Logger PULL_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.pullLog");
public static final Logger PULL_CHECK_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.pullCheckLog");
/**
* Dump log.
*/
public static final Logger DUMP_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.dumpLog");
public static final Logger MEMORY_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.monitorLog");
public static final Logger CLIENT_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.clientLog");
public static final Logger TRACE_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.traceLog");
public static final Logger NOTIFY_LOG = LoggerFactory.getLogger("com.alibaba.nacos.auth.notifyLog");
public static void setLogLevel(String logName, String level) {
switch (logName) {
case "config-server":
((ch.qos.logback.classic.Logger) DEFAULT_LOG).setLevel(Level.valueOf(level));
break;
case "config-fatal":
((ch.qos.logback.classic.Logger) FATAL_LOG).setLevel(Level.valueOf(level));
break;
case "config-pull":
((ch.qos.logback.classic.Logger) PULL_LOG).setLevel(Level.valueOf(level));
break;
case "config-pull-check":
((ch.qos.logback.classic.Logger) PULL_CHECK_LOG).setLevel(Level.valueOf(level));
break;
case "config-dump":
((ch.qos.logback.classic.Logger) DUMP_LOG).setLevel(Level.valueOf(level));
break;
case "config-memory":
((ch.qos.logback.classic.Logger) MEMORY_LOG).setLevel(Level.valueOf(level));
break;
case "config-client-request":
((ch.qos.logback.classic.Logger) CLIENT_LOG).setLevel(Level.valueOf(level));
break;
case "config-trace":
((ch.qos.logback.classic.Logger) TRACE_LOG).setLevel(Level.valueOf(level));
break;
case "config-notify":
((ch.qos.logback.classic.Logger) NOTIFY_LOG).setLevel(Level.valueOf(level));
break;
default:
break;
}
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.util;
import java.util.Objects;
/**
* Check precondition, throws an {@code IllegalArgumentException} If the conditions are not met.
* @author zzq
* @date 2021/7/29
*/
public class Preconditions {
/**
* check precondition.
* @param expression a boolean expression
* @param errorMessage the exception message to use if the check fails
* @throws IllegalArgumentException if {@code expression} is false
*/
public static void checkArgument(boolean expression, Object errorMessage) {
if (Objects.isNull(errorMessage)) {
throw new IllegalArgumentException("errorMessage cannot be null.");
}
if (!expression) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
}
/**
* check precondition.
* @param expression a boolean expression
* @param errorMessageTemplate the exception message template to use if the check fails
* @param errorMessageArgs the arguments to be substituted into the message template.
* @throws IllegalArgumentException if {@code expression} is false
*/
public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
if (Objects.isNull(errorMessageArgs) || Objects.isNull(errorMessageTemplate)) {
throw new IllegalArgumentException("errorMessageTemplate or errorMessage cannot be null.");
}
if (!expression) {
throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs));
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist;
import com.alibaba.nacos.auth.model.Page;
import com.alibaba.nacos.auth.model.PermissionInfo;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import com.alibaba.nacos.auth.persist.repository.externel.ExternalStoragePersistServiceImpl;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import java.lang.reflect.Field;
@RunWith(MockitoJUnitRunner.class)
public class ExternalPermissionPersistServiceImplTest {
@Mock
private ExternalStoragePersistServiceImpl externalStoragePersistService;
@Mock
private JdbcTemplate jdbcTemplate;
@Mock
private PaginationHelper paginationHelper;
private ExternalPermissionPersistServiceImpl externalPermissionPersistService;
@Before
public void setUp() throws Exception {
externalPermissionPersistService = new ExternalPermissionPersistServiceImpl();
Class<ExternalPermissionPersistServiceImpl> externalPermissionPersistServiceClass = ExternalPermissionPersistServiceImpl.class;
Field persistServiceClassDeclaredField = externalPermissionPersistServiceClass
.getDeclaredField("persistService");
persistServiceClassDeclaredField.setAccessible(true);
persistServiceClassDeclaredField.set(externalPermissionPersistService, externalStoragePersistService);
Mockito.when(externalStoragePersistService.getJdbcTemplate()).thenReturn(jdbcTemplate);
Mockito.when(externalStoragePersistService.createPaginationHelper()).thenReturn(paginationHelper);
externalPermissionPersistService.init();
}
@Test
public void testGetPermissions() {
Page<PermissionInfo> role = externalPermissionPersistService.getPermissions("role", 1, 10);
Assert.assertNotNull(role);
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist;
import com.alibaba.nacos.auth.model.Page;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import com.alibaba.nacos.auth.persist.repository.externel.ExternalStoragePersistServiceImpl;
import com.alibaba.nacos.auth.roles.RoleInfo;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import java.lang.reflect.Field;
@RunWith(MockitoJUnitRunner.class)
public class ExternalRolePersistServiceImplTest {
@Mock
private ExternalStoragePersistServiceImpl persistService;
@Mock
private JdbcTemplate jt;
@Mock
private PaginationHelper paginationHelper;
private ExternalRolePersistServiceImpl externalRolePersistService;
@Before
public void setUp() throws Exception {
externalRolePersistService = new ExternalRolePersistServiceImpl();
Class<ExternalRolePersistServiceImpl> externalRolePersistServiceClass = ExternalRolePersistServiceImpl.class;
Field persistServiceClassDeclaredField = externalRolePersistServiceClass.getDeclaredField("persistService");
persistServiceClassDeclaredField.setAccessible(true);
persistServiceClassDeclaredField.set(externalRolePersistService, persistService);
Mockito.when(persistService.getJdbcTemplate()).thenReturn(jt);
Mockito.when(persistService.createPaginationHelper()).thenReturn(paginationHelper);
externalRolePersistService.init();
}
@Test
public void testGetRolesByUserName() {
Page<RoleInfo> userName = externalRolePersistService.getRolesByUserName("userName", 1, 10);
Assert.assertNull(userName);
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist;
import com.alibaba.nacos.auth.model.Page;
import com.alibaba.nacos.auth.users.User;
import com.alibaba.nacos.auth.persist.repository.PaginationHelper;
import com.alibaba.nacos.auth.persist.repository.externel.ExternalStoragePersistServiceImpl;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import java.lang.reflect.Field;
@RunWith(MockitoJUnitRunner.class)
public class ExternalUserPersistServiceImplTest {
@Mock
private ExternalStoragePersistServiceImpl persistService;
@Mock
private JdbcTemplate jdbcTemplate;
@Mock
private PaginationHelper paginationHelper;
private ExternalUserPersistServiceImpl externalUserPersistService;
@Before
public void setUp() throws Exception {
externalUserPersistService = new ExternalUserPersistServiceImpl();
Class<ExternalUserPersistServiceImpl> externalUserPersistServiceClass = ExternalUserPersistServiceImpl.class;
Field persistServiceClassDeclaredField = externalUserPersistServiceClass.getDeclaredField("persistService");
persistServiceClassDeclaredField.setAccessible(true);
persistServiceClassDeclaredField.set(externalUserPersistService, persistService);
Mockito.when(persistService.getJdbcTemplate()).thenReturn(jdbcTemplate);
Mockito.when(persistService.createPaginationHelper()).thenReturn(paginationHelper);
externalUserPersistService.init();
}
@Test
public void testCreateUser() {
externalUserPersistService.createUser("username", "password");
String sql = "INSERT INTO users (username, password, enabled) VALUES (?, ?, ?)";
Mockito.verify(jdbcTemplate).update(sql, "username", "password", true);
}
@Test
public void testFindUserByUsername() {
User username = externalUserPersistService.findUserByUsername("username");
Assert.assertNull(username);
}
@Test
public void testGetUsers() {
Page<User> users = externalUserPersistService.getUsers(1, 10);
Assert.assertNotNull(users);
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.env.MockEnvironment;
import static org.junit.Assert.assertEquals;
public class DataSourcePoolPropertiesTest {
private static final String JDBC_URL = "jdbc:derby://127.0.0.1:3306/nacos_devtest?characterEncoding=utf8&serverTimezone=UTC";
private static final String JDBC_DRIVER_CLASS_NAME = "org.apache.derby.jdbc.EmbeddedDriver";
private static final String PASSWORD = "nacos";
private static final String USERNAME = "nacos_devtest";
private static final Long CONNECTION_TIMEOUT = 10000L;
private static final Integer MAX_POOL_SIZE = 50;
private MockEnvironment environment;
@Before
public void setUp() throws Exception {
environment = new MockEnvironment();
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.pool.config.connectionTimeout", CONNECTION_TIMEOUT.toString());
environment.setProperty("db.pool.config.maximumPoolSize", MAX_POOL_SIZE.toString());
}
@Test
public void testBuild() {
DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(environment);
poolProperties.setJdbcUrl(JDBC_URL);
// poolProperties.setDriverClassName(JDBC_DRIVER_CLASS_NAME);
poolProperties.setUsername(USERNAME);
poolProperties.setPassword(PASSWORD);
HikariDataSource actual = poolProperties.getDataSource();
assertEquals(JDBC_URL, actual.getJdbcUrl());
assertEquals(JDBC_DRIVER_CLASS_NAME, actual.getDriverClassName());
assertEquals(USERNAME, actual.getUsername());
assertEquals(PASSWORD, actual.getPassword());
assertEquals(CONNECTION_TIMEOUT.longValue(), actual.getConnectionTimeout());
assertEquals(DataSourcePoolProperties.DEFAULT_VALIDATION_TIMEOUT, actual.getValidationTimeout());
assertEquals(MAX_POOL_SIZE.intValue(), actual.getMaximumPoolSize());
assertEquals(DataSourcePoolProperties.DEFAULT_MINIMUM_IDLE, actual.getMinimumIdle());
}
}

View File

@ -0,0 +1,144 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.auth.persist.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.mock.env.MockEnvironment;
import java.util.List;
public class ExternalDataSourcePropertiesTest {
@SuppressWarnings("checkstyle:linelength")
public static final String JDBC_URL = "jdbc:mysql://127.0.0.1:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC";
public static final String PASSWORD = "nacos";
public static final String USERNAME = "nacos_devtest";
@Test
public void externalDatasourceNormally() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "1");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
Assert.assertEquals(dataSources.size(), 1);
}
@Test
public void externalDatasourceToAssertMultiJdbcUrl() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "2");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
environment.setProperty("db.url.1", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
Assert.assertEquals(dataSources.size(), 2);
}
@Test
public void externalDatasourceToAssertMultiPasswordAndUsername() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "2");
environment.setProperty("db.user.0", USERNAME);
environment.setProperty("db.user.1", USERNAME);
environment.setProperty("db.password.0", PASSWORD);
environment.setProperty("db.password.1", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
environment.setProperty("db.url.1", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
Assert.assertEquals(dataSources.size(), 2);
}
@Test
public void externalDatasourceToAssertMinIdle() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("db.num", "1");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
dataSource.validate();
Assert.assertEquals(dataSource.getMinimumIdle(), DataSourcePoolProperties.DEFAULT_MINIMUM_IDLE);
}));
Assert.assertEquals(dataSources.size(), 1);
}
@Test(expected = IllegalArgumentException.class)
public void externalDatasourceFailureWithLarkInfo() {
MockEnvironment environment = new MockEnvironment();
new ExternalDataSourceProperties().build(environment, null);
}
@Test(expected = IllegalArgumentException.class)
public void externalDatasourceFailureWithErrorInfo() {
HikariDataSource expectedDataSource = new HikariDataSource();
expectedDataSource.setJdbcUrl(JDBC_URL);
expectedDataSource.setUsername(USERNAME);
expectedDataSource.setPassword(PASSWORD);
MockEnvironment environment = new MockEnvironment();
// error num of db
environment.setProperty("db.num", "2");
environment.setProperty("db.user", USERNAME);
environment.setProperty("db.password", PASSWORD);
environment.setProperty("db.url.0", JDBC_URL);
List<HikariDataSource> dataSources = new ExternalDataSourceProperties().build(environment, (dataSource -> {
Assert.assertEquals(dataSource.getJdbcUrl(), expectedDataSource.getJdbcUrl());
Assert.assertEquals(dataSource.getUsername(), expectedDataSource.getUsername());
Assert.assertEquals(dataSource.getPassword(), expectedDataSource.getPassword());
}));
}
}