BasePluginService.java
Home
/
dao /
src /
main /
java /
org /
thingsboard /
server /
dao /
plugin /
BasePluginService.java
/**
* Copyright © 2016 The Thingsboard Authors
*
* 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 org.thingsboard.server.dao.plugin;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.dao.component.ComponentDescriptorService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DatabaseException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.PluginMetaDataEntity;
import org.thingsboard.server.dao.model.RuleMetaDataEntity;
import org.thingsboard.server.dao.rule.RuleDao;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
@Service
@Slf4j
public class BasePluginService implements PluginService {
//TODO: move to a better place.
public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
@Autowired
private PluginDao pluginDao;
@Autowired
private RuleDao ruleDao;
@Autowired
private ComponentDescriptorService componentDescriptorService;
@Override
public PluginMetaData savePlugin(PluginMetaData plugin) {
pluginValidator.validate(plugin);
if (plugin.getTenantId() == null) {
log.trace("Save system plugin metadata with predefined id {}", SYSTEM_TENANT);
plugin.setTenantId(SYSTEM_TENANT);
}
if (plugin.getId() != null) {
PluginMetaData oldVersion = getData(pluginDao.findById(plugin.getId()));
if (plugin.getState() == null) {
plugin.setState(oldVersion.getState());
} else if (plugin.getState() != oldVersion.getState()) {
throw new IncorrectParameterException("Use Activate/Suspend method to control state of the plugin!");
}
} else {
if (plugin.getState() == null) {
plugin.setState(ComponentLifecycleState.SUSPENDED);
} else if (plugin.getState() != ComponentLifecycleState.SUSPENDED) {
throw new IncorrectParameterException("Use Activate/Suspend method to control state of the plugin!");
}
}
ComponentDescriptor descriptor = componentDescriptorService.findByClazz(plugin.getClazz());
if (descriptor == null) {
throw new IncorrectParameterException("Plugin descriptor not found!");
} else if (!ComponentType.PLUGIN.equals(descriptor.getType())) {
throw new IncorrectParameterException("Plugin class is actually " + descriptor.getType() + "!");
}
PluginMetaDataEntity entity = pluginDao.findByApiToken(plugin.getApiToken());
if (entity != null && (plugin.getId() == null || !entity.getId().equals(plugin.getId().getId()))) {
throw new IncorrectParameterException("API token is already reserved!");
}
if (!componentDescriptorService.validate(descriptor, plugin.getConfiguration())) {
throw new IncorrectParameterException("Filters configuration is not valid!");
}
return getData(pluginDao.save(plugin));
}
@Override
public PluginMetaData findPluginById(PluginId pluginId) {
Validator.validateId(pluginId, "Incorrect plugin id for search request.");
return getData(pluginDao.findById(pluginId));
}
@Override
public PluginMetaData findPluginByApiToken(String apiToken) {
Validator.validateString(apiToken, "Incorrect plugin apiToken for search request.");
return getData(pluginDao.findByApiToken(apiToken));
}
@Override
public TextPageData<PluginMetaData> findSystemPlugins(TextPageLink pageLink) {
Validator.validatePageLink(pageLink, "Incorrect PageLink object for search system plugin request.");
List<PluginMetaDataEntity> pluginEntities = pluginDao.findByTenantIdAndPageLink(SYSTEM_TENANT, pageLink);
List<PluginMetaData> plugins = convertDataList(pluginEntities);
return new TextPageData<>(plugins, pageLink);
}
@Override
public TextPageData<PluginMetaData> findTenantPlugins(TenantId tenantId, TextPageLink pageLink) {
Validator.validateId(tenantId, "Incorrect tenant id for search plugins request.");
Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin request.");
List<PluginMetaDataEntity> pluginEntities = pluginDao.findByTenantIdAndPageLink(tenantId, pageLink);
List<PluginMetaData> plugins = convertDataList(pluginEntities);
return new TextPageData<>(plugins, pageLink);
}
@Override
public List<PluginMetaData> findSystemPlugins() {
log.trace("Executing findSystemPlugins");
List<PluginMetaData> plugins = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(300);
TextPageData<PluginMetaData> pageData = null;
do {
pageData = findSystemPlugins(pageLink);
plugins.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
return plugins;
}
@Override
public TextPageData<PluginMetaData> findAllTenantPluginsByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink) {
log.trace("Executing findAllTenantPluginsByTenantIdAndPageLink, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<PluginMetaDataEntity> pluginsEntities = pluginDao.findAllTenantPluginsByTenantId(tenantId.getId(), pageLink);
List<PluginMetaData> plugins = convertDataList(pluginsEntities);
return new TextPageData<>(plugins, pageLink);
}
@Override
public List<PluginMetaData> findAllTenantPluginsByTenantId(TenantId tenantId) {
log.trace("Executing findAllTenantPluginsByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
List<PluginMetaData> plugins = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(300);
TextPageData<PluginMetaData> pageData = null;
do {
pageData = findAllTenantPluginsByTenantIdAndPageLink(tenantId, pageLink);
plugins.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
return plugins;
}
@Override
public void activatePluginById(PluginId pluginId) {
updateLifeCycleState(pluginId, ComponentLifecycleState.ACTIVE);
}
@Override
public void suspendPluginById(PluginId pluginId) {
PluginMetaDataEntity plugin = pluginDao.findById(pluginId);
List<RuleMetaDataEntity> affectedRules = ruleDao.findRulesByPlugin(plugin.getApiToken())
.stream().filter(rule -> rule.getState() == ComponentLifecycleState.ACTIVE).collect(Collectors.toList());
if (affectedRules.isEmpty()) {
updateLifeCycleState(pluginId, ComponentLifecycleState.SUSPENDED);
} else {
throw new DataValidationException("Can't suspend plugin that has active rules!");
}
}
private void updateLifeCycleState(PluginId pluginId, ComponentLifecycleState state) {
Validator.validateId(pluginId, "Incorrect plugin id for state change request.");
PluginMetaDataEntity plugin = pluginDao.findById(pluginId);
if (plugin != null) {
plugin.setState(state);
pluginDao.save(plugin);
} else {
throw new DatabaseException("Plugin not found!");
}
}
@Override
public void deletePluginById(PluginId pluginId) {
Validator.validateId(pluginId, "Incorrect plugin id for delete request.");
checkRulesAndDelete(pluginId.getId());
}
private void checkRulesAndDelete(UUID pluginId) {
PluginMetaDataEntity plugin = pluginDao.findById(pluginId);
List<RuleMetaDataEntity> affectedRules = ruleDao.findRulesByPlugin(plugin.getApiToken());
if (affectedRules.isEmpty()) {
pluginDao.deleteById(pluginId);
} else {
throw new DataValidationException("Plugin deletion will affect existing rules!");
}
}
@Override
public void deletePluginsByTenantId(TenantId tenantId) {
Validator.validateId(tenantId, "Incorrect tenant id for delete plugins request.");
tenantPluginRemover.removeEntitites(tenantId);
}
private DataValidator<PluginMetaData> pluginValidator =
new DataValidator<PluginMetaData>() {
@Override
protected void validateDataImpl(PluginMetaData plugin) {
if (StringUtils.isEmpty(plugin.getName())) {
throw new DataValidationException("Plugin name should be specified!.");
}
if (StringUtils.isEmpty(plugin.getClazz())) {
throw new DataValidationException("Plugin clazz should be specified!.");
}
if (StringUtils.isEmpty(plugin.getApiToken())) {
throw new DataValidationException("Plugin api token is not set!");
}
if (plugin.getConfiguration() == null) {
throw new DataValidationException("Plugin configuration is not set!");
}
}
};
private PaginatedRemover<TenantId, PluginMetaDataEntity> tenantPluginRemover =
new PaginatedRemover<TenantId, PluginMetaDataEntity>() {
@Override
protected List<PluginMetaDataEntity> findEntities(TenantId id, TextPageLink pageLink) {
return pluginDao.findByTenantIdAndPageLink(id, pageLink);
}
@Override
protected void removeEntity(PluginMetaDataEntity entity) {
checkRulesAndDelete(entity.getId());
}
};
}