diff options
Diffstat (limited to 'src/main/java/org/traccar/database')
-rw-r--r-- | src/main/java/org/traccar/database/BufferingManager.java | 115 | ||||
-rw-r--r-- | src/main/java/org/traccar/database/GroupTree.java | 151 |
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); - } - } - } - -} |