aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar/database')
-rw-r--r--src/main/java/org/traccar/database/BufferingManager.java115
-rw-r--r--src/main/java/org/traccar/database/GroupTree.java151
2 files changed, 115 insertions, 151 deletions
diff --git a/src/main/java/org/traccar/database/BufferingManager.java b/src/main/java/org/traccar/database/BufferingManager.java
new file mode 100644
index 000000000..4d288c8d0
--- /dev/null
+++ b/src/main/java/org/traccar/database/BufferingManager.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * 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.traccar.database;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Position;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
+
+public class BufferingManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BufferingManager.class);
+
+ public interface Callback {
+ void onReleased(ChannelHandlerContext context, Position position);
+ }
+
+ private static final class Holder implements Comparable<Holder> {
+
+ private final ChannelHandlerContext context;
+ private final Position position;
+ private Timeout timeout;
+
+ private Holder(ChannelHandlerContext context, Position position) {
+ this.context = context;
+ this.position = position;
+ }
+
+ private int compareTime(Date left, Date right) {
+ if (left != null && right != null) {
+ return left.compareTo(right);
+ }
+ return 0;
+ }
+
+ @Override
+ public int compareTo(Holder other) {
+ int fixTimeResult = compareTime(position.getFixTime(), other.position.getFixTime());
+ if (fixTimeResult != 0) {
+ return fixTimeResult;
+ }
+
+ int deviceTimeResult = compareTime(position.getDeviceTime(), other.position.getDeviceTime());
+ if (deviceTimeResult != 0) {
+ return deviceTimeResult;
+ }
+
+ return position.getServerTime().compareTo(other.position.getServerTime());
+ }
+ }
+
+ private final Timer timer = new HashedWheelTimer();
+ private final Callback callback;
+ private final long threshold;
+
+ private final Map<Long, TreeSet<Holder>> buffer = new HashMap<>();
+
+ public BufferingManager(Config config, Callback callback) {
+ this.callback = callback;
+ threshold = config.getLong(Keys.SERVER_BUFFERING_THRESHOLD);
+ }
+
+ private Timeout scheduleTimeout(Holder holder) {
+ return timer.newTimeout(
+ timeout -> {
+ LOGGER.info("released {}", holder.position.getFixTime());
+ buffer.get(holder.position.getDeviceId()).remove(holder);
+ callback.onReleased(holder.context, holder.position);
+ },
+ threshold, TimeUnit.MILLISECONDS);
+ }
+
+ public void accept(ChannelHandlerContext context, Position position) {
+ if (threshold > 0) {
+ synchronized (buffer) {
+ LOGGER.info("queued {}", position.getFixTime());
+ var queue = buffer.computeIfAbsent(position.getDeviceId(), k -> new TreeSet<>());
+ Holder holder = new Holder(context, position);
+ holder.timeout = scheduleTimeout(holder);
+ queue.add(holder);
+ queue.tailSet(holder).forEach(h -> {
+ h.timeout.cancel();
+ h.timeout = scheduleTimeout(h);
+ });
+ }
+ } else {
+ callback.onReleased(context, position);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/GroupTree.java b/src/main/java/org/traccar/database/GroupTree.java
deleted file mode 100644
index 8798f55bc..000000000
--- a/src/main/java/org/traccar/database/GroupTree.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * 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.traccar.database;
-
-import org.traccar.model.Device;
-import org.traccar.model.Group;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class GroupTree {
-
- private static class TreeNode {
-
- private Group group;
- private Device device;
- private Collection<TreeNode> children = new HashSet<>();
-
- TreeNode(Group group) {
- this.group = group;
- }
-
- TreeNode(Device device) {
- this.device = device;
- }
-
- @Override
- public int hashCode() {
- if (group != null) {
- return (int) group.getId();
- } else {
- return (int) device.getId();
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof TreeNode)) {
- return false;
- }
- TreeNode other = (TreeNode) obj;
- if (other == this) {
- return true;
- }
- if (group != null && other.group != null) {
- return group.getId() == other.group.getId();
- } else if (device != null && other.device != null) {
- return device.getId() == other.device.getId();
- }
- return false;
- }
-
- public Group getGroup() {
- return group;
- }
-
- public Device getDevice() {
- return device;
- }
-
- public void setParent(TreeNode parent) {
- if (parent != null) {
- parent.children.add(this);
- }
- }
-
- public Collection<TreeNode> getChildren() {
- return children;
- }
-
- }
-
- private final Map<Long, TreeNode> groupMap = new HashMap<>();
-
- public GroupTree(Collection<Group> groups, Collection<Device> devices) {
-
- for (Group group : groups) {
- groupMap.put(group.getId(), new TreeNode(group));
- }
-
- for (TreeNode node : groupMap.values()) {
- if (node.getGroup().getGroupId() != 0) {
- node.setParent(groupMap.get(node.getGroup().getGroupId()));
- }
- }
-
- Map<Long, TreeNode> deviceMap = new HashMap<>();
-
- for (Device device : devices) {
- deviceMap.put(device.getId(), new TreeNode(device));
- }
-
- for (TreeNode node : deviceMap.values()) {
- if (node.getDevice().getGroupId() != 0) {
- node.setParent(groupMap.get(node.getDevice().getGroupId()));
- }
- }
-
- }
-
- public Collection<Group> getGroups(long groupId) {
- Set<TreeNode> results = new HashSet<>();
- getNodes(results, groupMap.get(groupId));
- Collection<Group> groups = new ArrayList<>();
- for (TreeNode node : results) {
- if (node.getGroup() != null) {
- groups.add(node.getGroup());
- }
- }
- return groups;
- }
-
- public Collection<Device> getDevices(long groupId) {
- Set<TreeNode> results = new HashSet<>();
- getNodes(results, groupMap.get(groupId));
- Collection<Device> devices = new ArrayList<>();
- for (TreeNode node : results) {
- if (node.getDevice() != null) {
- devices.add(node.getDevice());
- }
- }
- return devices;
- }
-
- private void getNodes(Set<TreeNode> results, TreeNode node) {
- if (node != null) {
- for (TreeNode child : node.getChildren()) {
- results.add(child);
- getNodes(results, child);
- }
- }
- }
-
-}