Commit dda61383933cac9aa6821a77ff9b19291e69db9f
Committed by
GitHub
Merge pull request #5328 from ViacheslavKlimov/bulk-import-improvements
Concurrent bulk import processing
Showing
16 changed files
with
152 additions
and
71 deletions
... | ... | @@ -133,7 +133,7 @@ public class AssetController extends BaseController { |
133 | 133 | |
134 | 134 | Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); |
135 | 135 | |
136 | - onAssetCreatedOrUpdated(savedAsset, asset.getId() != null); | |
136 | + onAssetCreatedOrUpdated(savedAsset, asset.getId() != null, getCurrentUser()); | |
137 | 137 | |
138 | 138 | return savedAsset; |
139 | 139 | } catch (Exception e) { |
... | ... | @@ -143,9 +143,9 @@ public class AssetController extends BaseController { |
143 | 143 | } |
144 | 144 | } |
145 | 145 | |
146 | - private void onAssetCreatedOrUpdated(Asset asset, boolean updated) { | |
146 | + private void onAssetCreatedOrUpdated(Asset asset, boolean updated, SecurityUser user) { | |
147 | 147 | try { |
148 | - logEntityAction(asset.getId(), asset, | |
148 | + logEntityAction(user, asset.getId(), asset, | |
149 | 149 | asset.getCustomerId(), |
150 | 150 | updated ? ActionType.UPDATED : ActionType.ADDED, null); |
151 | 151 | } catch (ThingsboardException e) { |
... | ... | @@ -648,8 +648,9 @@ public class AssetController extends BaseController { |
648 | 648 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
649 | 649 | @PostMapping("/asset/bulk_import") |
650 | 650 | public BulkImportResult<Asset> processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { |
651 | - return assetBulkImportService.processBulkImport(request, getCurrentUser(), importedAssetInfo -> { | |
652 | - onAssetCreatedOrUpdated(importedAssetInfo.getEntity(), importedAssetInfo.isUpdated()); | |
651 | + SecurityUser user = getCurrentUser(); | |
652 | + return assetBulkImportService.processBulkImport(request, user, importedAssetInfo -> { | |
653 | + onAssetCreatedOrUpdated(importedAssetInfo.getEntity(), importedAssetInfo.isUpdated(), user); | |
653 | 654 | }); |
654 | 655 | } |
655 | 656 | ... | ... |
... | ... | @@ -161,7 +161,7 @@ public class DeviceController extends BaseController { |
161 | 161 | |
162 | 162 | Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); |
163 | 163 | |
164 | - onDeviceCreatedOrUpdated(savedDevice, oldDevice, !created); | |
164 | + onDeviceCreatedOrUpdated(savedDevice, oldDevice, !created, getCurrentUser()); | |
165 | 165 | |
166 | 166 | return savedDevice; |
167 | 167 | } catch (Exception e) { |
... | ... | @@ -172,11 +172,11 @@ public class DeviceController extends BaseController { |
172 | 172 | |
173 | 173 | } |
174 | 174 | |
175 | - private void onDeviceCreatedOrUpdated(Device savedDevice, Device oldDevice, boolean updated) { | |
175 | + private void onDeviceCreatedOrUpdated(Device savedDevice, Device oldDevice, boolean updated, SecurityUser user) { | |
176 | 176 | tbClusterService.onDeviceUpdated(savedDevice, oldDevice); |
177 | 177 | |
178 | 178 | try { |
179 | - logEntityAction(savedDevice.getId(), savedDevice, | |
179 | + logEntityAction(user, savedDevice.getId(), savedDevice, | |
180 | 180 | savedDevice.getCustomerId(), |
181 | 181 | updated ? ActionType.UPDATED : ActionType.ADDED, null); |
182 | 182 | } catch (ThingsboardException e) { |
... | ... | @@ -941,8 +941,9 @@ public class DeviceController extends BaseController { |
941 | 941 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
942 | 942 | @PostMapping("/device/bulk_import") |
943 | 943 | public BulkImportResult<Device> processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception { |
944 | - return deviceBulkImportService.processBulkImport(request, getCurrentUser(), importedDeviceInfo -> { | |
945 | - onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), importedDeviceInfo.isUpdated()); | |
944 | + SecurityUser user = getCurrentUser(); | |
945 | + return deviceBulkImportService.processBulkImport(request, user, importedDeviceInfo -> { | |
946 | + onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), importedDeviceInfo.isUpdated(), user); | |
946 | 947 | }); |
947 | 948 | } |
948 | 949 | ... | ... |
... | ... | @@ -140,7 +140,7 @@ public class EdgeController extends BaseController { |
140 | 140 | edge.getId(), edge); |
141 | 141 | |
142 | 142 | Edge savedEdge = checkNotNull(edgeService.saveEdge(edge, true)); |
143 | - onEdgeCreatedOrUpdated(tenantId, savedEdge, edgeTemplateRootRuleChain, !created); | |
143 | + onEdgeCreatedOrUpdated(tenantId, savedEdge, edgeTemplateRootRuleChain, !created, getCurrentUser()); | |
144 | 144 | |
145 | 145 | return savedEdge; |
146 | 146 | } catch (Exception e) { |
... | ... | @@ -150,7 +150,7 @@ public class EdgeController extends BaseController { |
150 | 150 | } |
151 | 151 | } |
152 | 152 | |
153 | - private void onEdgeCreatedOrUpdated(TenantId tenantId, Edge edge, RuleChain edgeTemplateRootRuleChain, boolean updated) throws IOException, ThingsboardException { | |
153 | + private void onEdgeCreatedOrUpdated(TenantId tenantId, Edge edge, RuleChain edgeTemplateRootRuleChain, boolean updated, SecurityUser user) throws IOException, ThingsboardException { | |
154 | 154 | if (!updated) { |
155 | 155 | ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), edge.getId()); |
156 | 156 | edgeNotificationService.setEdgeRootRuleChain(tenantId, edge, edgeTemplateRootRuleChain.getId()); |
... | ... | @@ -160,7 +160,7 @@ public class EdgeController extends BaseController { |
160 | 160 | tbClusterService.broadcastEntityStateChangeEvent(edge.getTenantId(), edge.getId(), |
161 | 161 | updated ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED); |
162 | 162 | |
163 | - logEntityAction(edge.getId(), edge, null, updated ? ActionType.UPDATED : ActionType.ADDED, null); | |
163 | + logEntityAction(user, edge.getId(), edge, null, updated ? ActionType.UPDATED : ActionType.ADDED, null); | |
164 | 164 | } |
165 | 165 | |
166 | 166 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
... | ... | @@ -586,7 +586,7 @@ public class EdgeController extends BaseController { |
586 | 586 | |
587 | 587 | return edgeBulkImportService.processBulkImport(request, user, importedAssetInfo -> { |
588 | 588 | try { |
589 | - onEdgeCreatedOrUpdated(user.getTenantId(), importedAssetInfo.getEntity(), edgeTemplateRootRuleChain, importedAssetInfo.isUpdated()); | |
589 | + onEdgeCreatedOrUpdated(user.getTenantId(), importedAssetInfo.getEntity(), edgeTemplateRootRuleChain, importedAssetInfo.isUpdated(), user); | |
590 | 590 | } catch (Exception e) { |
591 | 591 | throw new RuntimeException(e); |
592 | 592 | } | ... | ... |
... | ... | @@ -63,6 +63,8 @@ import java.util.Map; |
63 | 63 | import java.util.Objects; |
64 | 64 | import java.util.Optional; |
65 | 65 | import java.util.Set; |
66 | +import java.util.concurrent.locks.Lock; | |
67 | +import java.util.concurrent.locks.ReentrantLock; | |
66 | 68 | |
67 | 69 | @Service |
68 | 70 | @TbCoreComponent |
... | ... | @@ -71,6 +73,8 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> { |
71 | 73 | protected final DeviceCredentialsService deviceCredentialsService; |
72 | 74 | protected final DeviceProfileService deviceProfileService; |
73 | 75 | |
76 | + private final Lock findOrCreateDeviceProfileLock = new ReentrantLock(); | |
77 | + | |
74 | 78 | public DeviceBulkImportService(TelemetrySubscriptionService tsSubscriptionService, TbTenantProfileCache tenantProfileCache, |
75 | 79 | AccessControlService accessControlService, AccessValidator accessValidator, |
76 | 80 | EntityActionService entityActionService, TbClusterService clusterService, |
... | ... | @@ -106,9 +110,13 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> { |
106 | 110 | throw new DeviceCredentialsValidationException("Invalid device credentials: " + e.getMessage()); |
107 | 111 | } |
108 | 112 | |
113 | + DeviceProfile deviceProfile; | |
109 | 114 | if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) { |
110 | - setUpLwM2mDeviceProfile(user.getTenantId(), device); | |
115 | + deviceProfile = setUpLwM2mDeviceProfile(user.getTenantId(), device); | |
116 | + } else { | |
117 | + deviceProfile = deviceProfileService.findOrCreateDeviceProfile(user.getTenantId(), device.getType()); | |
111 | 118 | } |
119 | + device.setDeviceProfileId(deviceProfile.getId()); | |
112 | 120 | |
113 | 121 | device = deviceService.saveDeviceWithCredentials(device, deviceCredentials); |
114 | 122 | |
... | ... | @@ -215,36 +223,43 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> { |
215 | 223 | credentials.setCredentialsValue(lwm2mCredentials.toString()); |
216 | 224 | } |
217 | 225 | |
218 | - private void setUpLwM2mDeviceProfile(TenantId tenantId, Device device) { | |
226 | + private DeviceProfile setUpLwM2mDeviceProfile(TenantId tenantId, Device device) { | |
219 | 227 | DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByName(tenantId, device.getType()); |
220 | 228 | if (deviceProfile != null) { |
221 | 229 | if (deviceProfile.getTransportType() != DeviceTransportType.LWM2M) { |
222 | 230 | deviceProfile.setTransportType(DeviceTransportType.LWM2M); |
223 | 231 | deviceProfile.getProfileData().setTransportConfiguration(new Lwm2mDeviceProfileTransportConfiguration()); |
224 | 232 | deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); |
225 | - device.setDeviceProfileId(deviceProfile.getId()); | |
226 | 233 | } |
227 | 234 | } else { |
228 | - deviceProfile = new DeviceProfile(); | |
229 | - deviceProfile.setTenantId(tenantId); | |
230 | - deviceProfile.setType(DeviceProfileType.DEFAULT); | |
231 | - deviceProfile.setName(device.getType()); | |
232 | - deviceProfile.setTransportType(DeviceTransportType.LWM2M); | |
233 | - deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); | |
235 | + findOrCreateDeviceProfileLock.lock(); | |
236 | + try { | |
237 | + deviceProfile = deviceProfileService.findDeviceProfileByName(tenantId, device.getType()); | |
238 | + if (deviceProfile == null) { | |
239 | + deviceProfile = new DeviceProfile(); | |
240 | + deviceProfile.setTenantId(tenantId); | |
241 | + deviceProfile.setType(DeviceProfileType.DEFAULT); | |
242 | + deviceProfile.setName(device.getType()); | |
243 | + deviceProfile.setTransportType(DeviceTransportType.LWM2M); | |
244 | + deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); | |
234 | 245 | |
235 | - DeviceProfileData deviceProfileData = new DeviceProfileData(); | |
236 | - DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | |
237 | - DeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); | |
238 | - DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); | |
246 | + DeviceProfileData deviceProfileData = new DeviceProfileData(); | |
247 | + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | |
248 | + DeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); | |
249 | + DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); | |
239 | 250 | |
240 | - deviceProfileData.setConfiguration(configuration); | |
241 | - deviceProfileData.setTransportConfiguration(transportConfiguration); | |
242 | - deviceProfileData.setProvisionConfiguration(provisionConfiguration); | |
243 | - deviceProfile.setProfileData(deviceProfileData); | |
251 | + deviceProfileData.setConfiguration(configuration); | |
252 | + deviceProfileData.setTransportConfiguration(transportConfiguration); | |
253 | + deviceProfileData.setProvisionConfiguration(provisionConfiguration); | |
254 | + deviceProfile.setProfileData(deviceProfileData); | |
244 | 255 | |
245 | - deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); | |
246 | - device.setDeviceProfileId(deviceProfile.getId()); | |
256 | + deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); | |
257 | + } | |
258 | + } finally { | |
259 | + findOrCreateDeviceProfileLock.unlock(); | |
260 | + } | |
247 | 261 | } |
262 | + return deviceProfile; | |
248 | 263 | } |
249 | 264 | |
250 | 265 | private void setValues(ObjectNode objectNode, Map<BulkImportColumnType, String> data, Collection<BulkImportColumnType> columns) { | ... | ... |
... | ... | @@ -22,6 +22,9 @@ import lombok.Data; |
22 | 22 | import lombok.RequiredArgsConstructor; |
23 | 23 | import lombok.SneakyThrows; |
24 | 24 | import org.apache.commons.lang3.StringUtils; |
25 | +import org.apache.commons.lang3.exception.ExceptionUtils; | |
26 | +import org.thingsboard.common.util.DonAsynchron; | |
27 | +import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
25 | 28 | import org.thingsboard.server.cluster.TbClusterService; |
26 | 29 | import org.thingsboard.server.common.data.BaseData; |
27 | 30 | import org.thingsboard.server.common.data.TenantProfile; |
... | ... | @@ -47,11 +50,16 @@ import org.thingsboard.server.utils.CsvUtils; |
47 | 50 | import org.thingsboard.server.utils.TypeCastUtil; |
48 | 51 | |
49 | 52 | import javax.annotation.Nullable; |
53 | +import javax.annotation.PostConstruct; | |
54 | +import javax.annotation.PreDestroy; | |
50 | 55 | import java.util.ArrayList; |
51 | 56 | import java.util.Arrays; |
52 | 57 | import java.util.LinkedHashMap; |
53 | 58 | import java.util.List; |
54 | 59 | import java.util.Map; |
60 | +import java.util.concurrent.CountDownLatch; | |
61 | +import java.util.concurrent.LinkedBlockingQueue; | |
62 | +import java.util.concurrent.ThreadPoolExecutor; | |
55 | 63 | import java.util.concurrent.TimeUnit; |
56 | 64 | import java.util.concurrent.atomic.AtomicInteger; |
57 | 65 | import java.util.function.Consumer; |
... | ... | @@ -67,39 +75,49 @@ public abstract class AbstractBulkImportService<E extends BaseData<? extends Ent |
67 | 75 | protected final EntityActionService entityActionService; |
68 | 76 | protected final TbClusterService clusterService; |
69 | 77 | |
70 | - public final BulkImportResult<E> processBulkImport(BulkImportRequest request, SecurityUser user, Consumer<ImportedEntityInfo<E>> onEntityImported) throws Exception { | |
71 | - BulkImportResult<E> result = new BulkImportResult<>(); | |
78 | + private static ThreadPoolExecutor executor; | |
72 | 79 | |
73 | - AtomicInteger i = new AtomicInteger(0); | |
74 | - if (request.getMapping().getHeader()) { | |
75 | - i.incrementAndGet(); | |
80 | + @PostConstruct | |
81 | + private void initExecutor() { | |
82 | + if (executor == null) { | |
83 | + executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(), | |
84 | + 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(150_000), | |
85 | + ThingsBoardThreadFactory.forName("bulk-import"), new ThreadPoolExecutor.CallerRunsPolicy()); | |
86 | + executor.allowCoreThreadTimeOut(true); | |
76 | 87 | } |
88 | + } | |
77 | 89 | |
78 | - parseData(request).forEach(entityData -> { | |
79 | - i.incrementAndGet(); | |
80 | - try { | |
81 | - ImportedEntityInfo<E> importedEntityInfo = saveEntity(request, entityData.getFields(), user); | |
82 | - onEntityImported.accept(importedEntityInfo); | |
90 | + public final BulkImportResult<E> processBulkImport(BulkImportRequest request, SecurityUser user, Consumer<ImportedEntityInfo<E>> onEntityImported) throws Exception { | |
91 | + List<EntityData> entitiesData = parseData(request); | |
83 | 92 | |
84 | - E entity = importedEntityInfo.getEntity(); | |
93 | + BulkImportResult<E> result = new BulkImportResult<>(); | |
94 | + CountDownLatch completionLatch = new CountDownLatch(entitiesData.size()); | |
85 | 95 | |
86 | - saveKvs(user, entity, entityData.getKvs()); | |
96 | + entitiesData.forEach(entityData -> DonAsynchron.submit(() -> { | |
97 | + ImportedEntityInfo<E> importedEntityInfo = saveEntity(request, entityData.getFields(), user); | |
98 | + E entity = importedEntityInfo.getEntity(); | |
87 | 99 | |
88 | - if (importedEntityInfo.getRelatedError() != null) { | |
89 | - throw new RuntimeException(importedEntityInfo.getRelatedError()); | |
90 | - } | |
100 | + onEntityImported.accept(importedEntityInfo); | |
101 | + saveKvs(user, entity, entityData.getKvs()); | |
91 | 102 | |
92 | - if (importedEntityInfo.isUpdated()) { | |
93 | - result.setUpdated(result.getUpdated() + 1); | |
94 | - } else { | |
95 | - result.setCreated(result.getCreated() + 1); | |
96 | - } | |
97 | - } catch (Exception e) { | |
98 | - result.setErrors(result.getErrors() + 1); | |
99 | - result.getErrorsList().add(String.format("Line %d: %s", i.get(), e.getMessage())); | |
100 | - } | |
101 | - }); | |
103 | + return importedEntityInfo; | |
104 | + }, | |
105 | + importedEntityInfo -> { | |
106 | + if (importedEntityInfo.isUpdated()) { | |
107 | + result.getUpdated().incrementAndGet(); | |
108 | + } else { | |
109 | + result.getCreated().incrementAndGet(); | |
110 | + } | |
111 | + completionLatch.countDown(); | |
112 | + }, | |
113 | + throwable -> { | |
114 | + result.getErrors().incrementAndGet(); | |
115 | + result.getErrorsList().add(String.format("Line %d: %s", entityData.getLineNumber(), ExceptionUtils.getRootCauseMessage(throwable))); | |
116 | + completionLatch.countDown(); | |
117 | + }, | |
118 | + executor)); | |
102 | 119 | |
120 | + completionLatch.await(); | |
103 | 121 | return result; |
104 | 122 | } |
105 | 123 | |
... | ... | @@ -186,8 +204,11 @@ public abstract class AbstractBulkImportService<E extends BaseData<? extends Ent |
186 | 204 | |
187 | 205 | private List<EntityData> parseData(BulkImportRequest request) throws Exception { |
188 | 206 | List<List<String>> records = CsvUtils.parseCsv(request.getFile(), request.getMapping().getDelimiter()); |
207 | + AtomicInteger linesCounter = new AtomicInteger(0); | |
208 | + | |
189 | 209 | if (request.getMapping().getHeader()) { |
190 | 210 | records.remove(0); |
211 | + linesCounter.incrementAndGet(); | |
191 | 212 | } |
192 | 213 | |
193 | 214 | List<ColumnMapping> columnsMappings = request.getMapping().getColumns(); |
... | ... | @@ -205,15 +226,24 @@ public abstract class AbstractBulkImportService<E extends BaseData<? extends Ent |
205 | 226 | entityData.getKvs().put(entry.getKey(), new ParsedValue(castResult.getValue(), castResult.getKey())); |
206 | 227 | } |
207 | 228 | }); |
229 | + entityData.setLineNumber(linesCounter.incrementAndGet()); | |
208 | 230 | return entityData; |
209 | 231 | }) |
210 | 232 | .collect(Collectors.toList()); |
211 | 233 | } |
212 | 234 | |
235 | + @PreDestroy | |
236 | + private void shutdownExecutor() { | |
237 | + if (!executor.isTerminating()) { | |
238 | + executor.shutdown(); | |
239 | + } | |
240 | + } | |
241 | + | |
213 | 242 | @Data |
214 | 243 | protected static class EntityData { |
215 | 244 | private final Map<BulkImportColumnType, String> fields = new LinkedHashMap<>(); |
216 | 245 | private final Map<ColumnMapping, ParsedValue> kvs = new LinkedHashMap<>(); |
246 | + private int lineNumber; | |
217 | 247 | } |
218 | 248 | |
219 | 249 | @Data | ... | ... |
... | ... | @@ -17,14 +17,14 @@ package org.thingsboard.server.service.importing; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | |
20 | -import java.util.LinkedList; | |
21 | -import java.util.List; | |
20 | +import java.util.Collection; | |
21 | +import java.util.concurrent.ConcurrentLinkedDeque; | |
22 | +import java.util.concurrent.atomic.AtomicInteger; | |
22 | 23 | |
23 | 24 | @Data |
24 | 25 | public class BulkImportResult<E> { |
25 | - private int created = 0; | |
26 | - private int updated = 0; | |
27 | - private int errors = 0; | |
28 | - private List<String> errorsList = new LinkedList<>(); | |
29 | - | |
26 | + private AtomicInteger created = new AtomicInteger(); | |
27 | + private AtomicInteger updated = new AtomicInteger(); | |
28 | + private AtomicInteger errors = new AtomicInteger(); | |
29 | + private Collection<String> errorsList = new ConcurrentLinkedDeque<>(); | |
30 | 30 | } | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.Futures; |
20 | 20 | import com.google.common.util.concurrent.ListenableFuture; |
21 | 21 | import com.google.common.util.concurrent.MoreExecutors; |
22 | 22 | |
23 | +import java.util.concurrent.Callable; | |
23 | 24 | import java.util.concurrent.Executor; |
24 | 25 | import java.util.function.Consumer; |
25 | 26 | |
... | ... | @@ -53,4 +54,15 @@ public class DonAsynchron { |
53 | 54 | Futures.addCallback(future, callback, MoreExecutors.directExecutor()); |
54 | 55 | } |
55 | 56 | } |
57 | + | |
58 | + public static <T> ListenableFuture<T> submit(Callable<T> task, Consumer<T> onSuccess, Consumer<Throwable> onFailure, Executor executor) { | |
59 | + return submit(task, onSuccess, onFailure, executor, null); | |
60 | + } | |
61 | + | |
62 | + public static <T> ListenableFuture<T> submit(Callable<T> task, Consumer<T> onSuccess, Consumer<Throwable> onFailure, Executor executor, Executor callbackExecutor) { | |
63 | + ListenableFuture<T> future = Futures.submit(task, executor); | |
64 | + withCallback(future, onSuccess, onFailure, callbackExecutor); | |
65 | + return future; | |
66 | + } | |
67 | + | |
56 | 68 | } | ... | ... |
... | ... | @@ -35,6 +35,8 @@ public interface DeviceCredentialsDao extends Dao<DeviceCredentials> { |
35 | 35 | */ |
36 | 36 | DeviceCredentials save(TenantId tenantId, DeviceCredentials deviceCredentials); |
37 | 37 | |
38 | + DeviceCredentials saveAndFlush(TenantId tenantId, DeviceCredentials deviceCredentials); | |
39 | + | |
38 | 40 | /** |
39 | 41 | * Find device credentials by device id. |
40 | 42 | * | ... | ... |
... | ... | @@ -96,7 +96,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen |
96 | 96 | log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials); |
97 | 97 | credentialsValidator.validate(deviceCredentials, id -> tenantId); |
98 | 98 | try { |
99 | - return deviceCredentialsDao.save(tenantId, deviceCredentials); | |
99 | + return deviceCredentialsDao.saveAndFlush(tenantId, deviceCredentials); | |
100 | 100 | } catch (Exception t) { |
101 | 101 | ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
102 | 102 | if (e != null && e.getConstraintName() != null | ... | ... |
... | ... | @@ -30,6 +30,8 @@ public interface DeviceProfileDao extends Dao<DeviceProfile> { |
30 | 30 | |
31 | 31 | DeviceProfile save(TenantId tenantId, DeviceProfile deviceProfile); |
32 | 32 | |
33 | + DeviceProfile saveAndFlush(TenantId tenantId, DeviceProfile deviceProfile); | |
34 | + | |
33 | 35 | PageData<DeviceProfile> findDeviceProfiles(TenantId tenantId, PageLink pageLink); |
34 | 36 | |
35 | 37 | PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType); | ... | ... |
... | ... | @@ -167,7 +167,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
167 | 167 | } |
168 | 168 | DeviceProfile savedDeviceProfile; |
169 | 169 | try { |
170 | - savedDeviceProfile = deviceProfileDao.save(deviceProfile.getTenantId(), deviceProfile); | |
170 | + savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile); | |
171 | 171 | } catch (Exception t) { |
172 | 172 | ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
173 | 173 | if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) { | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.sql.device; |
17 | 17 | |
18 | -import org.springframework.data.repository.CrudRepository; | |
18 | +import org.springframework.data.jpa.repository.JpaRepository; | |
19 | 19 | import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity; |
20 | 20 | |
21 | 21 | import java.util.UUID; |
... | ... | @@ -23,7 +23,7 @@ import java.util.UUID; |
23 | 23 | /** |
24 | 24 | * Created by Valerii Sosliuk on 5/6/2017. |
25 | 25 | */ |
26 | -public interface DeviceCredentialsRepository extends CrudRepository<DeviceCredentialsEntity, UUID> { | |
26 | +public interface DeviceCredentialsRepository extends JpaRepository<DeviceCredentialsEntity, UUID> { | |
27 | 27 | |
28 | 28 | DeviceCredentialsEntity findByDeviceId(UUID deviceId); |
29 | 29 | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.device; |
17 | 17 | |
18 | 18 | import org.springframework.data.domain.Page; |
19 | 19 | import org.springframework.data.domain.Pageable; |
20 | +import org.springframework.data.jpa.repository.JpaRepository; | |
20 | 21 | import org.springframework.data.jpa.repository.Query; |
21 | 22 | import org.springframework.data.repository.PagingAndSortingRepository; |
22 | 23 | import org.springframework.data.repository.query.Param; |
... | ... | @@ -26,7 +27,7 @@ import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; |
26 | 27 | |
27 | 28 | import java.util.UUID; |
28 | 29 | |
29 | -public interface DeviceProfileRepository extends PagingAndSortingRepository<DeviceProfileEntity, UUID> { | |
30 | +public interface DeviceProfileRepository extends JpaRepository<DeviceProfileEntity, UUID> { | |
30 | 31 | |
31 | 32 | @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " + |
32 | 33 | "FROM DeviceProfileEntity d " + | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.device; |
18 | 18 | import org.springframework.beans.factory.annotation.Autowired; |
19 | 19 | import org.springframework.data.repository.CrudRepository; |
20 | 20 | import org.springframework.stereotype.Component; |
21 | +import org.springframework.transaction.annotation.Transactional; | |
21 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 23 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
23 | 24 | import org.thingsboard.server.dao.DaoUtil; |
... | ... | @@ -46,6 +47,14 @@ public class JpaDeviceCredentialsDao extends JpaAbstractDao<DeviceCredentialsEnt |
46 | 47 | return deviceCredentialsRepository; |
47 | 48 | } |
48 | 49 | |
50 | + @Transactional | |
51 | + @Override | |
52 | + public DeviceCredentials saveAndFlush(TenantId tenantId, DeviceCredentials deviceCredentials) { | |
53 | + DeviceCredentials result = save(tenantId, deviceCredentials); | |
54 | + deviceCredentialsRepository.flush(); | |
55 | + return result; | |
56 | + } | |
57 | + | |
49 | 58 | @Override |
50 | 59 | public DeviceCredentials findByDeviceId(TenantId tenantId, UUID deviceId) { |
51 | 60 | return DaoUtil.getData(deviceCredentialsRepository.findByDeviceId(deviceId)); | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils; |
19 | 19 | import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.data.repository.CrudRepository; |
21 | 21 | import org.springframework.stereotype.Component; |
22 | +import org.springframework.transaction.annotation.Transactional; | |
22 | 23 | import org.thingsboard.server.common.data.DeviceProfile; |
23 | 24 | import org.thingsboard.server.common.data.DeviceProfileInfo; |
24 | 25 | import org.thingsboard.server.common.data.DeviceTransportType; |
... | ... | @@ -54,6 +55,14 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao<DeviceProfileE |
54 | 55 | return deviceProfileRepository.findDeviceProfileInfoById(deviceProfileId); |
55 | 56 | } |
56 | 57 | |
58 | + @Transactional | |
59 | + @Override | |
60 | + public DeviceProfile saveAndFlush(TenantId tenantId, DeviceProfile deviceProfile) { | |
61 | + DeviceProfile result = save(tenantId, deviceProfile); | |
62 | + deviceProfileRepository.flush(); | |
63 | + return result; | |
64 | + } | |
65 | + | |
57 | 66 | @Override |
58 | 67 | public PageData<DeviceProfile> findDeviceProfiles(TenantId tenantId, PageLink pageLink) { |
59 | 68 | return DaoUtil.toPageData( | ... | ... |