aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar')
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java16
-rw-r--r--src/main/java/org/traccar/ExtendedObjectDecoder.java12
-rw-r--r--src/main/java/org/traccar/MainEventHandler.java3
-rw-r--r--src/main/java/org/traccar/config/Keys.java7
-rw-r--r--src/main/java/org/traccar/handler/AcknowledgementHandler.java121
-rw-r--r--src/main/java/org/traccar/handler/FilterHandler.java23
6 files changed, 168 insertions, 14 deletions
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index b184da45c..70f999c72 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2023 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.
@@ -25,6 +25,7 @@ import io.netty.channel.ChannelPipeline;
import io.netty.handler.timeout.IdleStateHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.handler.AcknowledgementHandler;
import org.traccar.handler.ComputedAttributesHandler;
import org.traccar.handler.CopyAttributesHandler;
import org.traccar.handler.DefaultDataHandler;
@@ -60,15 +61,19 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
private final Injector injector;
private final TrackerConnector connector;
private final String protocol;
- private int timeout;
+ private final boolean instantAcknowledgement;
+ private final int timeout;
public BasePipelineFactory(TrackerConnector connector, Config config, String protocol) {
this.injector = Main.getInjector();
this.connector = connector;
this.protocol = protocol;
- timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
+ instantAcknowledgement = config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT);
+ int timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
if (timeout == 0) {
- timeout = config.getInteger(Keys.SERVER_TIMEOUT);
+ this.timeout = config.getInteger(Keys.SERVER_TIMEOUT);
+ } else {
+ this.timeout = timeout;
}
}
@@ -112,6 +117,9 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
pipeline.addLast(new OpenChannelHandler(connector));
pipeline.addLast(new NetworkMessageHandler());
pipeline.addLast(new StandardLoggingHandler(protocol));
+ if (!instantAcknowledgement) {
+ pipeline.addLast(new AcknowledgementHandler());
+ }
addProtocolHandlers(handler -> {
if (handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder) {
diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java
index f79a36c85..805f98cb7 100644
--- a/src/main/java/org/traccar/ExtendedObjectDecoder.java
+++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2023 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.
@@ -23,6 +23,7 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.handler.AcknowledgementHandler;
import org.traccar.helper.DataConverter;
import org.traccar.model.Position;
@@ -30,6 +31,7 @@ import javax.inject.Inject;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
+import java.util.List;
public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter {
@@ -68,6 +70,7 @@ public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
NetworkMessage networkMessage = (NetworkMessage) msg;
Object originalMessage = networkMessage.getMessage();
+ ctx.writeAndFlush(new AcknowledgementHandler.EventReceived());
try {
Object decodedMessage = decode(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage);
onMessageEvent(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage, decodedMessage);
@@ -76,14 +79,19 @@ public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter
}
if (decodedMessage != null) {
if (decodedMessage instanceof Collection) {
- for (Object o : (Collection) decodedMessage) {
+ var collection = (Collection) decodedMessage;
+ ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(collection));
+ for (Object o : collection) {
saveOriginal(o, originalMessage);
ctx.fireChannelRead(o);
}
} else {
+ ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(List.of(decodedMessage)));
saveOriginal(decodedMessage, originalMessage);
ctx.fireChannelRead(decodedMessage);
}
+ } else {
+ ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(List.of()));
}
} finally {
ReferenceCountUtil.release(originalMessage);
diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java
index 877f03ae7..658ff6d6d 100644
--- a/src/main/java/org/traccar/MainEventHandler.java
+++ b/src/main/java/org/traccar/MainEventHandler.java
@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.database.StatisticsManager;
+import org.traccar.handler.AcknowledgementHandler;
import org.traccar.helper.DateUtil;
import org.traccar.helper.NetworkUtil;
import org.traccar.helper.model.PositionUtil;
@@ -145,6 +146,8 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter {
LOGGER.info(builder.toString());
statisticsManager.registerMessageStored(position.getDeviceId(), position.getProtocol());
+
+ ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position));
}
}
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index cb1ee63e1..c207efb1e 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -293,6 +293,13 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
+ * Send device responses immediately before writing it in the database.
+ */
+ public static final ConfigKey<Boolean> SERVER_INSTANT_ACKNOWLEDGEMENT = new BooleanConfigKey(
+ "server.instantAcknowledgement",
+ List.of(KeyType.CONFIG));
+
+ /**
* Address for uploading aggregated anonymous usage statistics. Uploaded information is the same you can see on the
* statistics screen in the web app. It does not include any sensitive (e.g. locations).
*/
diff --git a/src/main/java/org/traccar/handler/AcknowledgementHandler.java b/src/main/java/org/traccar/handler/AcknowledgementHandler.java
new file mode 100644
index 000000000..c3f6038de
--- /dev/null
+++ b/src/main/java/org/traccar/handler/AcknowledgementHandler.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2023 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.handler;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class AcknowledgementHandler extends ChannelOutboundHandlerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AcknowledgementHandler.class);
+
+ public interface Event {
+ }
+
+ public static class EventReceived implements Event {
+ }
+
+ public static class EventDecoded implements Event {
+ private final Collection<Object> objects;
+
+ public EventDecoded(Collection<Object> objects) {
+ this.objects = objects;
+ }
+
+ public Collection<Object> getObjects() {
+ return objects;
+ }
+ }
+
+ public static class EventHandled implements Event {
+ private final Object object;
+
+ public EventHandled(Object object) {
+ this.object = object;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+ }
+
+ private static class Entry {
+ private final Object message;
+ private final ChannelPromise promise;
+
+ private Entry(Object message, ChannelPromise promise) {
+ this.message = message;
+ this.promise = promise;
+ }
+
+ public Object getMessage() {
+ return message;
+ }
+
+ public ChannelPromise getPromise() {
+ return promise;
+ }
+ }
+
+ private List<Entry> queue;
+ private final Set<Object> waiting = new HashSet<>();
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ List<Entry> output = new LinkedList<>();
+ synchronized (this) {
+ if (msg instanceof Event) {
+ if (msg instanceof EventReceived) {
+ LOGGER.debug("Event received");
+ if (queue == null) {
+ queue = new LinkedList<>();
+ }
+ } else if (msg instanceof EventDecoded) {
+ EventDecoded event = (EventDecoded) msg;
+ LOGGER.debug("Event decoded {}", event.getObjects().size());
+ waiting.addAll(event.getObjects());
+ } else if (msg instanceof EventHandled) {
+ EventHandled event = (EventHandled) msg;
+ LOGGER.debug("Event handled");
+ waiting.remove(event.getObject());
+ }
+ if (!(msg instanceof EventReceived) && waiting.isEmpty()) {
+ output.addAll(queue);
+ queue = null;
+ }
+ } else if (queue != null) {
+ LOGGER.debug("Message queued");
+ queue.add(new Entry(msg, promise));
+ } else {
+ LOGGER.debug("Message sent");
+ output.add(new Entry(msg, promise));
+ }
+ }
+ for (Entry entry : output) {
+ ctx.write(entry.getMessage(), entry.getPromise());
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java
index 994276bb6..1d1c27b7a 100644
--- a/src/main/java/org/traccar/handler/FilterHandler.java
+++ b/src/main/java/org/traccar/handler/FilterHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2023 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.
@@ -16,9 +16,10 @@
package org.traccar.handler;
import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.BaseDataHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.UnitsConverter;
@@ -39,7 +40,7 @@ import java.util.Date;
@Singleton
@ChannelHandler.Sharable
-public class FilterHandler extends BaseDataHandler {
+public class FilterHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(FilterHandler.class);
@@ -177,7 +178,7 @@ public class FilterHandler extends BaseDataHandler {
return false;
}
- private boolean filter(Position position) {
+ protected boolean filter(Position position) {
StringBuilder filterType = new StringBuilder();
@@ -243,11 +244,17 @@ public class FilterHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
- if (enabled && filter(position)) {
- return null;
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof Position) {
+ Position position = (Position) msg;
+ if (enabled && filter(position)) {
+ ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position));
+ } else {
+ ctx.fireChannelRead(position);
+ }
+ } else {
+ super.channelRead(ctx, msg);
}
- return position;
}
}