aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/org/traccar/BaseDataHandler.java38
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java97
-rw-r--r--src/main/java/org/traccar/BaseProtocolDecoder.java19
-rw-r--r--src/main/java/org/traccar/ExtendedObjectDecoder.java2
-rw-r--r--src/main/java/org/traccar/MainEventHandler.java194
-rw-r--r--src/main/java/org/traccar/MainModule.java31
-rw-r--r--src/main/java/org/traccar/ProcessingHandler.java203
-rw-r--r--src/main/java/org/traccar/ServerManager.java8
-rw-r--r--src/main/java/org/traccar/api/resource/DeviceResource.java34
-rw-r--r--src/main/java/org/traccar/api/resource/UserResource.java10
-rw-r--r--src/main/java/org/traccar/config/Keys.java8
-rw-r--r--src/main/java/org/traccar/database/BufferingManager.java115
-rw-r--r--src/main/java/org/traccar/database/GroupTree.java151
-rw-r--r--src/main/java/org/traccar/handler/BasePositionHandler.java (renamed from src/main/java/org/traccar/GlobalTimer.java)29
-rw-r--r--src/main/java/org/traccar/handler/ComputedAttributesHandler.java40
-rw-r--r--src/main/java/org/traccar/handler/CopyAttributesHandler.java34
-rw-r--r--src/main/java/org/traccar/handler/DatabaseHandler.java (renamed from src/main/java/org/traccar/handler/DefaultDataHandler.java)24
-rw-r--r--src/main/java/org/traccar/handler/DistanceHandler.java13
-rw-r--r--src/main/java/org/traccar/handler/EngineHoursHandler.java16
-rw-r--r--src/main/java/org/traccar/handler/FilterHandler.java27
-rw-r--r--src/main/java/org/traccar/handler/GeocoderHandler.java65
-rw-r--r--src/main/java/org/traccar/handler/GeofenceHandler.java15
-rw-r--r--src/main/java/org/traccar/handler/GeolocationHandler.java73
-rw-r--r--src/main/java/org/traccar/handler/HemisphereHandler.java16
-rw-r--r--src/main/java/org/traccar/handler/MotionHandler.java16
-rw-r--r--src/main/java/org/traccar/handler/OutdatedHandler.java56
-rw-r--r--src/main/java/org/traccar/handler/PositionForwardingHandler.java (renamed from src/main/java/org/traccar/PositionForwardingHandler.java)18
-rw-r--r--src/main/java/org/traccar/handler/PostProcessHandler.java67
-rw-r--r--src/main/java/org/traccar/handler/SpeedLimitHandler.java49
-rw-r--r--src/main/java/org/traccar/handler/TimeHandler.java31
-rw-r--r--src/main/java/org/traccar/handler/events/AlertEventHandler.java17
-rw-r--r--src/main/java/org/traccar/handler/events/BaseEventHandler.java32
-rw-r--r--src/main/java/org/traccar/handler/events/BehaviorEventHandler.java18
-rw-r--r--src/main/java/org/traccar/handler/events/CommandResultEventHandler.java17
-rw-r--r--src/main/java/org/traccar/handler/events/DriverEventHandler.java18
-rw-r--r--src/main/java/org/traccar/handler/events/FuelEventHandler.java22
-rw-r--r--src/main/java/org/traccar/handler/events/GeofenceEventHandler.java21
-rw-r--r--src/main/java/org/traccar/handler/events/IgnitionEventHandler.java25
-rw-r--r--src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java19
-rw-r--r--src/main/java/org/traccar/handler/events/MediaEventHandler.java16
-rw-r--r--src/main/java/org/traccar/handler/events/MotionEventHandler.java22
-rw-r--r--src/main/java/org/traccar/handler/events/OverspeedEventHandler.java27
-rw-r--r--src/main/java/org/traccar/handler/network/AcknowledgementHandler.java (renamed from src/main/java/org/traccar/handler/AcknowledgementHandler.java)2
-rw-r--r--src/main/java/org/traccar/handler/network/MainEventHandler.java98
-rw-r--r--src/main/java/org/traccar/handler/network/NetworkForwarderHandler.java (renamed from src/main/java/org/traccar/handler/NetworkForwarderHandler.java)2
-rw-r--r--src/main/java/org/traccar/handler/network/NetworkMessageHandler.java (renamed from src/main/java/org/traccar/handler/NetworkMessageHandler.java)2
-rw-r--r--src/main/java/org/traccar/handler/network/OpenChannelHandler.java (renamed from src/main/java/org/traccar/handler/OpenChannelHandler.java)2
-rw-r--r--src/main/java/org/traccar/handler/network/RemoteAddressHandler.java (renamed from src/main/java/org/traccar/handler/RemoteAddressHandler.java)8
-rw-r--r--src/main/java/org/traccar/handler/network/StandardLoggingHandler.java (renamed from src/main/java/org/traccar/handler/StandardLoggingHandler.java)2
-rw-r--r--src/main/java/org/traccar/helper/PositionLogger.java94
-rw-r--r--src/main/java/org/traccar/model/Device.java2
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java101
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java30
54 files changed, 1106 insertions, 995 deletions
diff --git a/src/main/java/org/traccar/BaseDataHandler.java b/src/main/java/org/traccar/BaseDataHandler.java
deleted file mode 100644
index 48794b0d7..000000000
--- a/src/main/java/org/traccar/BaseDataHandler.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2015 - 2018 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;
-
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import org.traccar.model.Position;
-
-public abstract class BaseDataHandler extends ChannelInboundHandlerAdapter {
-
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- if (msg instanceof Position) {
- Position position = handlePosition((Position) msg);
- if (position != null) {
- ctx.fireChannelRead(position);
- }
- } else {
- super.channelRead(ctx, msg);
- }
- }
-
- protected abstract Position handlePosition(Position position);
-
-}
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index ca4a4ae63..40360cca7 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -25,36 +25,13 @@ 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;
-import org.traccar.handler.DistanceHandler;
-import org.traccar.handler.EngineHoursHandler;
-import org.traccar.handler.FilterHandler;
-import org.traccar.handler.GeocoderHandler;
-import org.traccar.handler.GeofenceHandler;
-import org.traccar.handler.GeolocationHandler;
-import org.traccar.handler.HemisphereHandler;
-import org.traccar.handler.MotionHandler;
-import org.traccar.handler.NetworkForwarderHandler;
-import org.traccar.handler.NetworkMessageHandler;
-import org.traccar.handler.OpenChannelHandler;
-import org.traccar.handler.RemoteAddressHandler;
-import org.traccar.handler.SpeedLimitHandler;
-import org.traccar.handler.StandardLoggingHandler;
-import org.traccar.handler.TimeHandler;
-import org.traccar.handler.events.AlertEventHandler;
-import org.traccar.handler.events.BehaviorEventHandler;
-import org.traccar.handler.events.CommandResultEventHandler;
-import org.traccar.handler.events.DriverEventHandler;
-import org.traccar.handler.events.FuelEventHandler;
-import org.traccar.handler.events.GeofenceEventHandler;
-import org.traccar.handler.events.IgnitionEventHandler;
-import org.traccar.handler.events.MaintenanceEventHandler;
-import org.traccar.handler.events.MediaEventHandler;
-import org.traccar.handler.events.MotionEventHandler;
-import org.traccar.handler.events.OverspeedEventHandler;
+import org.traccar.handler.network.AcknowledgementHandler;
+import org.traccar.handler.network.MainEventHandler;
+import org.traccar.handler.network.NetworkForwarderHandler;
+import org.traccar.handler.network.NetworkMessageHandler;
+import org.traccar.handler.network.OpenChannelHandler;
+import org.traccar.handler.network.RemoteAddressHandler;
+import org.traccar.handler.network.StandardLoggingHandler;
import java.util.Map;
@@ -83,15 +60,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
protected abstract void addProtocolHandlers(PipelineBuilder pipeline);
- @SafeVarargs
- private void addHandlers(ChannelPipeline pipeline, Class<? extends ChannelHandler>... handlerClasses) {
- for (Class<? extends ChannelHandler> handlerClass : handlerClasses) {
- if (handlerClass != null) {
- pipeline.addLast(injector.getInstance(handlerClass));
- }
- }
- }
-
+ @SuppressWarnings("unchecked")
public static <T extends ChannelHandler> T getHandler(ChannelPipeline pipeline, Class<T> clazz) {
for (Map.Entry<String, ChannelHandler> handlerEntry : pipeline) {
ChannelHandler handler = handlerEntry.getValue();
@@ -107,6 +76,11 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
return null;
}
+ private <T> T injectMembers(T object) {
+ injector.injectMembers(object);
+ return object;
+ }
+
@Override
protected void initChannel(Channel channel) {
final ChannelPipeline pipeline = channel.pipeline();
@@ -119,15 +93,10 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
pipeline.addLast(new OpenChannelHandler(connector));
if (config.hasKey(Keys.SERVER_FORWARD)) {
int port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol));
- var handler = new NetworkForwarderHandler(port);
- injector.injectMembers(handler);
- pipeline.addLast(handler);
+ pipeline.addLast(injectMembers(new NetworkForwarderHandler(port)));
}
pipeline.addLast(new NetworkMessageHandler());
-
- var loggingHandler = new StandardLoggingHandler(protocol);
- injector.injectMembers(loggingHandler);
- pipeline.addLast(loggingHandler);
+ pipeline.addLast(injectMembers(new StandardLoggingHandler(protocol)));
if (!connector.isDatagram() && !config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT)) {
pipeline.addLast(new AcknowledgementHandler());
@@ -135,7 +104,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
addProtocolHandlers(handler -> {
if (handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder) {
- injector.injectMembers(handler);
+ injectMembers(handler);
} else {
if (handler instanceof ChannelInboundHandler) {
handler = new WrapperInboundHandler((ChannelInboundHandler) handler);
@@ -146,35 +115,9 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
pipeline.addLast(handler);
});
- addHandlers(
- pipeline,
- TimeHandler.class,
- GeolocationHandler.class,
- HemisphereHandler.class,
- DistanceHandler.class,
- RemoteAddressHandler.class,
- FilterHandler.class,
- GeofenceHandler.class,
- GeocoderHandler.class,
- SpeedLimitHandler.class,
- MotionHandler.class,
- CopyAttributesHandler.class,
- EngineHoursHandler.class,
- ComputedAttributesHandler.class,
- PositionForwardingHandler.class,
- DefaultDataHandler.class,
- MediaEventHandler.class,
- CommandResultEventHandler.class,
- OverspeedEventHandler.class,
- BehaviorEventHandler.class,
- FuelEventHandler.class,
- MotionEventHandler.class,
- GeofenceEventHandler.class,
- AlertEventHandler.class,
- IgnitionEventHandler.class,
- MaintenanceEventHandler.class,
- DriverEventHandler.class,
- MainEventHandler.class);
+ pipeline.addLast(injector.getInstance(RemoteAddressHandler.class));
+ pipeline.addLast(injector.getInstance(ProcessingHandler.class));
+ pipeline.addLast(injector.getInstance(MainEventHandler.class));
}
}
diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java
index 495a866c0..b764e5cdf 100644
--- a/src/main/java/org/traccar/BaseProtocolDecoder.java
+++ b/src/main/java/org/traccar/BaseProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -154,25 +154,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
public void getLastLocation(Position position, Date deviceTime) {
if (position.getDeviceId() != 0) {
position.setOutdated(true);
-
- Position last = cacheManager.getPosition(position.getDeviceId());
- if (last != null) {
- position.setFixTime(last.getFixTime());
- position.setValid(last.getValid());
- position.setLatitude(last.getLatitude());
- position.setLongitude(last.getLongitude());
- position.setAltitude(last.getAltitude());
- position.setSpeed(last.getSpeed());
- position.setCourse(last.getCourse());
- position.setAccuracy(last.getAccuracy());
- } else {
- position.setFixTime(new Date(0));
- }
-
if (deviceTime != null) {
position.setDeviceTime(deviceTime);
- } else {
- position.setDeviceTime(new Date());
}
}
}
diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java
index cddddcd80..9468e2fff 100644
--- a/src/main/java/org/traccar/ExtendedObjectDecoder.java
+++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java
@@ -23,7 +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.handler.network.AcknowledgementHandler;
import org.traccar.helper.DataConverter;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java
deleted file mode 100644
index fb0171d63..000000000
--- a/src/main/java/org/traccar/MainEventHandler.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2012 - 2022 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;
-
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.socket.DatagramChannel;
-import io.netty.handler.codec.http.HttpRequestDecoder;
-import io.netty.handler.timeout.IdleStateEvent;
-import org.slf4j.Logger;
-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;
-import org.traccar.model.Device;
-import org.traccar.model.Position;
-import org.traccar.session.ConnectionManager;
-import org.traccar.session.cache.CacheManager;
-import org.traccar.storage.Storage;
-import org.traccar.storage.StorageException;
-import org.traccar.storage.query.Columns;
-import org.traccar.storage.query.Condition;
-import org.traccar.storage.query.Request;
-
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-@Singleton
-@ChannelHandler.Sharable
-public class MainEventHandler extends ChannelInboundHandlerAdapter {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MainEventHandler.class);
-
- private final Set<String> connectionlessProtocols = new HashSet<>();
- private final Set<String> logAttributes = new LinkedHashSet<>();
-
- private final CacheManager cacheManager;
- private final Storage storage;
- private final ConnectionManager connectionManager;
- private final StatisticsManager statisticsManager;
-
- @Inject
- public MainEventHandler(
- Config config, CacheManager cacheManager, Storage storage, ConnectionManager connectionManager,
- StatisticsManager statisticsManager) {
- this.cacheManager = cacheManager;
- this.storage = storage;
- this.connectionManager = connectionManager;
- this.statisticsManager = statisticsManager;
- String connectionlessProtocolList = config.getString(Keys.STATUS_IGNORE_OFFLINE);
- if (connectionlessProtocolList != null) {
- connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split("[, ]")));
- }
- logAttributes.addAll(Arrays.asList(config.getString(Keys.LOGGER_ATTRIBUTES).split("[, ]")));
- }
-
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) {
- if (msg instanceof Position) {
-
- Position position = (Position) msg;
- Device device = cacheManager.getObject(Device.class, position.getDeviceId());
-
- try {
- if (PositionUtil.isLatest(cacheManager, position)) {
- Device updatedDevice = new Device();
- updatedDevice.setId(position.getDeviceId());
- updatedDevice.setPositionId(position.getId());
- storage.updateObject(updatedDevice, new Request(
- new Columns.Include("positionId"),
- new Condition.Equals("id", updatedDevice.getId())));
-
- cacheManager.updatePosition(position);
- connectionManager.updatePosition(true, position);
- }
- } catch (StorageException error) {
- LOGGER.warn("Failed to update device", error);
- }
-
- StringBuilder builder = new StringBuilder();
- builder.append("[").append(NetworkUtil.session(ctx.channel())).append("] ");
- builder.append("id: ").append(device.getUniqueId());
- for (String attribute : logAttributes) {
- switch (attribute) {
- case "time":
- builder.append(", time: ").append(DateUtil.formatDate(position.getFixTime(), false));
- break;
- case "position":
- builder.append(", lat: ").append(String.format("%.5f", position.getLatitude()));
- builder.append(", lon: ").append(String.format("%.5f", position.getLongitude()));
- break;
- case "speed":
- if (position.getSpeed() > 0) {
- builder.append(", speed: ").append(String.format("%.1f", position.getSpeed()));
- }
- break;
- case "course":
- builder.append(", course: ").append(String.format("%.1f", position.getCourse()));
- break;
- case "accuracy":
- if (position.getAccuracy() > 0) {
- builder.append(", accuracy: ").append(String.format("%.1f", position.getAccuracy()));
- }
- break;
- case "outdated":
- if (position.getOutdated()) {
- builder.append(", outdated");
- }
- break;
- case "invalid":
- if (!position.getValid()) {
- builder.append(", invalid");
- }
- break;
- default:
- Object value = position.getAttributes().get(attribute);
- if (value != null) {
- builder.append(", ").append(attribute).append(": ").append(value);
- }
- break;
- }
- }
- LOGGER.info(builder.toString());
-
- statisticsManager.registerMessageStored(position.getDeviceId(), position.getProtocol());
-
- ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position));
- }
- }
-
- @Override
- public void channelActive(ChannelHandlerContext ctx) {
- if (!(ctx.channel() instanceof DatagramChannel)) {
- LOGGER.info("[{}] connected", NetworkUtil.session(ctx.channel()));
- }
- }
-
- @Override
- public void channelInactive(ChannelHandlerContext ctx) {
- LOGGER.info("[{}] disconnected", NetworkUtil.session(ctx.channel()));
- closeChannel(ctx.channel());
-
- boolean supportsOffline = BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null
- && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName());
- connectionManager.deviceDisconnected(ctx.channel(), supportsOffline);
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
- while (cause.getCause() != null && cause.getCause() != cause) {
- cause = cause.getCause();
- }
- LOGGER.info("[{}] error", NetworkUtil.session(ctx.channel()), cause);
- closeChannel(ctx.channel());
- }
-
- @Override
- public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
- if (evt instanceof IdleStateEvent) {
- LOGGER.info("[{}] timed out", NetworkUtil.session(ctx.channel()));
- closeChannel(ctx.channel());
- }
- }
-
- private void closeChannel(Channel channel) {
- if (!(channel instanceof DatagramChannel)) {
- channel.close();
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 26654947e..791d61c61 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -72,9 +72,12 @@ import org.traccar.geolocation.GeolocationProvider;
import org.traccar.geolocation.GoogleGeolocationProvider;
import org.traccar.geolocation.OpenCellIdGeolocationProvider;
import org.traccar.geolocation.UnwiredGeolocationProvider;
+import org.traccar.handler.CopyAttributesHandler;
+import org.traccar.handler.FilterHandler;
import org.traccar.handler.GeocoderHandler;
import org.traccar.handler.GeolocationHandler;
import org.traccar.handler.SpeedLimitHandler;
+import org.traccar.handler.TimeHandler;
import org.traccar.helper.ObjectMapperContextResolver;
import org.traccar.helper.SanitizerModule;
import org.traccar.helper.WebHelper;
@@ -338,6 +341,34 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
+ public static CopyAttributesHandler provideCopyAttributesHandler(Config config, CacheManager cacheManager) {
+ if (config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE)) {
+ return new CopyAttributesHandler(config, cacheManager);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static FilterHandler provideFilterHandler(
+ Config config, CacheManager cacheManager, Storage storage, StatisticsManager statisticsManager) {
+ if (config.getBoolean(Keys.FILTER_ENABLE)) {
+ return new FilterHandler(config, cacheManager, storage, statisticsManager);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static TimeHandler provideTimeHandler(Config config) {
+ if (config.hasKey(Keys.TIME_OVERRIDE)) {
+ return new TimeHandler(config);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
public static BroadcastService provideBroadcastService(
Config config, ObjectMapper objectMapper) throws IOException {
if (config.hasKey(Keys.BROADCAST_TYPE)) {
diff --git a/src/main/java/org/traccar/ProcessingHandler.java b/src/main/java/org/traccar/ProcessingHandler.java
new file mode 100644
index 000000000..fd048d127
--- /dev/null
+++ b/src/main/java/org/traccar/ProcessingHandler.java
@@ -0,0 +1,203 @@
+/*
+ * 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;
+
+import com.google.inject.Injector;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.traccar.config.Config;
+import org.traccar.database.BufferingManager;
+import org.traccar.database.NotificationManager;
+import org.traccar.handler.BasePositionHandler;
+import org.traccar.handler.ComputedAttributesHandler;
+import org.traccar.handler.CopyAttributesHandler;
+import org.traccar.handler.DatabaseHandler;
+import org.traccar.handler.DistanceHandler;
+import org.traccar.handler.EngineHoursHandler;
+import org.traccar.handler.FilterHandler;
+import org.traccar.handler.GeocoderHandler;
+import org.traccar.handler.GeofenceHandler;
+import org.traccar.handler.GeolocationHandler;
+import org.traccar.handler.HemisphereHandler;
+import org.traccar.handler.MotionHandler;
+import org.traccar.handler.OutdatedHandler;
+import org.traccar.handler.PositionForwardingHandler;
+import org.traccar.handler.PostProcessHandler;
+import org.traccar.handler.SpeedLimitHandler;
+import org.traccar.handler.TimeHandler;
+import org.traccar.handler.events.AlertEventHandler;
+import org.traccar.handler.events.BaseEventHandler;
+import org.traccar.handler.events.BehaviorEventHandler;
+import org.traccar.handler.events.CommandResultEventHandler;
+import org.traccar.handler.events.DriverEventHandler;
+import org.traccar.handler.events.FuelEventHandler;
+import org.traccar.handler.events.GeofenceEventHandler;
+import org.traccar.handler.events.IgnitionEventHandler;
+import org.traccar.handler.events.MaintenanceEventHandler;
+import org.traccar.handler.events.MediaEventHandler;
+import org.traccar.handler.events.MotionEventHandler;
+import org.traccar.handler.events.OverspeedEventHandler;
+import org.traccar.handler.network.AcknowledgementHandler;
+import org.traccar.helper.PositionLogger;
+import org.traccar.model.Position;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Singleton
+@ChannelHandler.Sharable
+public class ProcessingHandler extends ChannelInboundHandlerAdapter implements BufferingManager.Callback {
+
+ private final NotificationManager notificationManager;
+ private final PositionLogger positionLogger;
+ private final BufferingManager bufferingManager;
+ private final List<BasePositionHandler> positionHandlers;
+ private final List<BaseEventHandler> eventHandlers;
+ private final PostProcessHandler postProcessHandler;
+
+ private final Map<Long, Queue<Position>> queues = new HashMap<>();
+
+ private synchronized Queue<Position> getQueue(long deviceId) {
+ return queues.computeIfAbsent(deviceId, k -> new LinkedList<>());
+ }
+
+ @Inject
+ public ProcessingHandler(
+ Injector injector, Config config, NotificationManager notificationManager, PositionLogger positionLogger) {
+ this.notificationManager = notificationManager;
+ this.positionLogger = positionLogger;
+ bufferingManager = new BufferingManager(config, this);
+
+ positionHandlers = Stream.of(
+ OutdatedHandler.class,
+ TimeHandler.class,
+ GeolocationHandler.class,
+ HemisphereHandler.class,
+ DistanceHandler.class,
+ FilterHandler.class,
+ GeofenceHandler.class,
+ GeocoderHandler.class,
+ SpeedLimitHandler.class,
+ MotionHandler.class,
+ EngineHoursHandler.class,
+ ComputedAttributesHandler.class,
+ CopyAttributesHandler.class,
+ PositionForwardingHandler.class,
+ DatabaseHandler.class)
+ .map((clazz) -> (BasePositionHandler) injector.getInstance(clazz))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toUnmodifiableList());
+
+ eventHandlers = Stream.of(
+ MediaEventHandler.class,
+ CommandResultEventHandler.class,
+ OverspeedEventHandler.class,
+ BehaviorEventHandler.class,
+ FuelEventHandler.class,
+ MotionEventHandler.class,
+ GeofenceEventHandler.class,
+ AlertEventHandler.class,
+ IgnitionEventHandler.class,
+ MaintenanceEventHandler.class,
+ DriverEventHandler.class)
+ .map((clazz) -> (BaseEventHandler) injector.getInstance(clazz))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toUnmodifiableList());
+
+ postProcessHandler = injector.getInstance(PostProcessHandler.class);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof Position) {
+ bufferingManager.accept(ctx, (Position) msg);
+ } else {
+ super.channelRead(ctx, msg);
+ }
+ }
+
+ @Override
+ public void onReleased(ChannelHandlerContext context, Position position) {
+ Queue<Position> queue = getQueue(position.getDeviceId());
+ boolean queued;
+ synchronized (queue) {
+ queued = !queue.isEmpty();
+ queue.offer(position);
+ }
+ if (!queued) {
+ processPositionHandlers(context, position);
+ }
+ }
+
+ private void processPositionHandlers(ChannelHandlerContext ctx, Position position) {
+ var iterator = positionHandlers.iterator();
+ iterator.next().handlePosition(position, new BasePositionHandler.Callback() {
+ @Override
+ public void processed(boolean filtered) {
+ if (!filtered) {
+ if (iterator.hasNext()) {
+ iterator.next().handlePosition(position, this);
+ } else {
+ processEventHandlers(ctx, position);
+ }
+ } else {
+ finishedProcessing(ctx, position, true);
+ }
+ }
+ });
+ }
+
+ private void processEventHandlers(ChannelHandlerContext ctx, Position position) {
+ eventHandlers.forEach(handler -> handler.analyzePosition(
+ position, (event) -> notificationManager.updateEvents(Map.of(event, position))));
+ finishedProcessing(ctx, position, false);
+ }
+
+ private void finishedProcessing(ChannelHandlerContext ctx, Position position, boolean filtered) {
+ if (!filtered) {
+ postProcessHandler.handlePosition(position, ignore -> {
+ positionLogger.log(ctx, position);
+ ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position));
+ processNextPosition(ctx, position.getDeviceId());
+ });
+ } else {
+ ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position));
+ processNextPosition(ctx, position.getDeviceId());
+ }
+ }
+
+ private void processNextPosition(ChannelHandlerContext ctx, long deviceId) {
+ Queue<Position> queue = getQueue(deviceId);
+ Position nextPosition;
+ synchronized (queue) {
+ queue.poll(); // remove current position
+ nextPosition = queue.peek();
+ }
+ if (nextPosition != null) {
+ processPositionHandlers(ctx, nextPosition);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java
index e91be50a2..22af66b41 100644
--- a/src/main/java/org/traccar/ServerManager.java
+++ b/src/main/java/org/traccar/ServerManager.java
@@ -82,12 +82,8 @@ public class ServerManager implements LifecycleObject {
@Override
public void stop() throws Exception {
- try {
- for (TrackerConnector connector : connectorList) {
- connector.stop();
- }
- } finally {
- GlobalTimer.release();
+ for (TrackerConnector connector : connectorList) {
+ connector.stop();
}
}
diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java
index 89bba7237..56253152f 100644
--- a/src/main/java/org/traccar/api/resource/DeviceResource.java
+++ b/src/main/java/org/traccar/api/resource/DeviceResource.java
@@ -62,6 +62,9 @@ import java.util.List;
@Consumes(MediaType.APPLICATION_JSON)
public class DeviceResource extends BaseObjectResource<Device> {
+ private static final int DEFAULT_BUFFER_SIZE = 8192;
+ private static final int IMAGE_SIZE_LIMIT = 500000;
+
@Inject
private Config config;
@@ -172,6 +175,23 @@ public class DeviceResource extends BaseObjectResource<Device> {
return Response.noContent().build();
}
+ private String imageExtension(String type) {
+ switch (type) {
+ case "image/jpeg":
+ return "jpg";
+ case "image/png":
+ return "png";
+ case "image/gif":
+ return "gif";
+ case "image/webp":
+ return "webp";
+ case "image/svg+xml":
+ return "svg";
+ default:
+ throw new IllegalArgumentException("Unsupported image type");
+ }
+ }
+
@Path("{id}/image")
@POST
@Consumes("image/*")
@@ -186,10 +206,20 @@ public class DeviceResource extends BaseObjectResource<Device> {
new Condition.Permission(User.class, getUserId(), Device.class))));
if (device != null) {
String name = "device";
- String extension = type.substring("image/".length());
+ String extension = imageExtension(type);
try (var input = new FileInputStream(file);
var output = mediaManager.createFileStream(device.getUniqueId(), name, extension)) {
- input.transferTo(output);
+
+ long transferred = 0;
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ int read;
+ while ((read = input.read(buffer, 0, buffer.length)) >= 0) {
+ output.write(buffer, 0, read);
+ transferred += read;
+ if (transferred > IMAGE_SIZE_LIMIT) {
+ throw new IllegalArgumentException("Image size limit exceeded");
+ }
+ }
}
return Response.ok(name + "." + extension).build();
}
diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java
index 47ea9b07c..fbc31e46a 100644
--- a/src/main/java/org/traccar/api/resource/UserResource.java
+++ b/src/main/java/org/traccar/api/resource/UserResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -95,7 +95,9 @@ public class UserResource extends BaseObjectResource<User> {
}
}
} else {
- if (!permissionsService.getServer().getRegistration()) {
+ if (UserUtil.isEmpty(storage)) {
+ entity.setAdministrator(true);
+ } else if (!permissionsService.getServer().getRegistration()) {
throw new SecurityException("Registration disabled");
}
if (permissionsService.getServer().getBoolean(Keys.WEB_TOTP_FORCE.getKey())
@@ -106,10 +108,6 @@ public class UserResource extends BaseObjectResource<User> {
}
}
- if (UserUtil.isEmpty(storage)) {
- entity.setAdministrator(true);
- }
-
entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id"))));
storage.updateObject(entity, new Request(
new Columns.Include("hashedPassword", "salt"),
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index 4aacb2cd8..d346084bd 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -293,6 +293,14 @@ public final class Keys {
false);
/**
+ * If not zero, enable buffering of incoming data to handle ordering locations. The value is threshold for
+ * buffering in milliseconds.
+ */
+ public static final ConfigKey<Long> SERVER_BUFFERING_THRESHOLD = new LongConfigKey(
+ "server.buffering.threshold",
+ List.of(KeyType.CONFIG));
+
+ /**
* Server wide connection timeout value in seconds. See protocol timeout for more information.
*/
public static final ConfigKey<Integer> SERVER_TIMEOUT = new IntegerConfigKey(
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);
- }
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/GlobalTimer.java b/src/main/java/org/traccar/handler/BasePositionHandler.java
index a97321ba2..0a82e96b7 100644
--- a/src/main/java/org/traccar/GlobalTimer.java
+++ b/src/main/java/org/traccar/handler/BasePositionHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -13,30 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler;
-import io.netty.util.HashedWheelTimer;
-import io.netty.util.Timer;
+import org.traccar.model.Position;
-public final class GlobalTimer {
+public abstract class BasePositionHandler {
- private static Timer instance = null;
-
- private GlobalTimer() {
- }
-
- public static void release() {
- if (instance != null) {
- instance.stop();
- }
- instance = null;
- }
-
- public static Timer getTimer() {
- if (instance == null) {
- instance = new HashedWheelTimer();
- }
- return instance;
+ public interface Callback {
+ void processed(boolean filtered);
}
+ public abstract void handlePosition(Position position, Callback callback);
}
diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
index 8b010ceae..4293bd1fc 100644
--- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,26 +16,15 @@
*/
package org.traccar.handler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.List;
-
-import io.netty.channel.ChannelHandler;
-import org.apache.commons.jexl3.JexlFeatures;
-import org.apache.commons.jexl3.JexlEngine;
+import jakarta.inject.Inject;
import org.apache.commons.jexl3.JexlBuilder;
-import org.apache.commons.jexl3.introspection.JexlSandbox;
+import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlFeatures;
import org.apache.commons.jexl3.MapContext;
+import org.apache.commons.jexl3.introspection.JexlSandbox;
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.model.Attribute;
@@ -43,12 +32,17 @@ import org.traccar.model.Device;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
-@Singleton
-@ChannelHandler.Sharable
-public class ComputedAttributesHandler extends BaseDataHandler {
+public class ComputedAttributesHandler extends BasePositionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ComputedAttributesHandler.class);
@@ -144,7 +138,7 @@ public class ComputedAttributesHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
Collection<Attribute> attributes = cacheManager.getDeviceObjects(position.getDeviceId(), Attribute.class);
for (Attribute attribute : attributes) {
if (attribute.getAttribute() != null) {
@@ -202,7 +196,7 @@ public class ComputedAttributesHandler extends BaseDataHandler {
}
}
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
index 42b438e41..c7452e58c 100644
--- a/src/main/java/org/traccar/handler/CopyAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,45 +16,35 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import org.traccar.BaseDataHandler;
+import jakarta.inject.Inject;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
-public class CopyAttributesHandler extends BaseDataHandler {
+public class CopyAttributesHandler extends BasePositionHandler {
- private final boolean enabled;
private final CacheManager cacheManager;
@Inject
public CopyAttributesHandler(Config config, CacheManager cacheManager) {
- enabled = config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE);
this.cacheManager = cacheManager;
}
@Override
- protected Position handlePosition(Position position) {
- if (enabled) {
- String attributesString = AttributeUtil.lookup(
- cacheManager, Keys.PROCESSING_COPY_ATTRIBUTES, position.getDeviceId());
- Position last = cacheManager.getPosition(position.getDeviceId());
- if (last != null && attributesString != null) {
- for (String attribute : attributesString.split("[ ,]")) {
- if (last.hasAttribute(attribute) && !position.hasAttribute(attribute)) {
- position.getAttributes().put(attribute, last.getAttributes().get(attribute));
- }
+ public void handlePosition(Position position, Callback callback) {
+ String attributesString = AttributeUtil.lookup(
+ cacheManager, Keys.PROCESSING_COPY_ATTRIBUTES, position.getDeviceId());
+ Position last = cacheManager.getPosition(position.getDeviceId());
+ if (last != null && attributesString != null) {
+ for (String attribute : attributesString.split("[ ,]")) {
+ if (last.hasAttribute(attribute) && !position.hasAttribute(attribute)) {
+ position.getAttributes().put(attribute, last.getAttributes().get(attribute));
}
}
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/DefaultDataHandler.java b/src/main/java/org/traccar/handler/DatabaseHandler.java
index cca6dcd0a..5d96ebb34 100644
--- a/src/main/java/org/traccar/handler/DefaultDataHandler.java
+++ b/src/main/java/org/traccar/handler/DatabaseHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -15,41 +15,39 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.BaseDataHandler;
+import org.traccar.database.StatisticsManager;
import org.traccar.model.Position;
import org.traccar.storage.Storage;
import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Request;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
-public class DefaultDataHandler extends BaseDataHandler {
+public class DatabaseHandler extends BasePositionHandler {
- private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDataHandler.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHandler.class);
private final Storage storage;
+ private final StatisticsManager statisticsManager;
@Inject
- public DefaultDataHandler(Storage storage) {
+ public DatabaseHandler(Storage storage, StatisticsManager statisticsManager) {
this.storage = storage;
+ this.statisticsManager = statisticsManager;
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
try {
position.setId(storage.addObject(position, new Request(new Columns.Exclude("id"))));
+ statisticsManager.messageStoredCount(position.getDeviceId());
} catch (Exception error) {
LOGGER.warn("Failed to store position", error);
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java
index db8c73779..e8ae7753a 100644
--- a/src/main/java/org/traccar/handler/DistanceHandler.java
+++ b/src/main/java/org/traccar/handler/DistanceHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2015 Amila Silva
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,14 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import org.traccar.BaseDataHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.DistanceCalculator;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-@Singleton
-@ChannelHandler.Sharable
-public class DistanceHandler extends BaseDataHandler {
+public class DistanceHandler extends BasePositionHandler {
private final CacheManager cacheManager;
@@ -45,7 +40,7 @@ public class DistanceHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
double distance = 0.0;
if (position.hasAttribute(Position.KEY_DISTANCE)) {
@@ -76,7 +71,7 @@ public class DistanceHandler extends BaseDataHandler {
position.set(Position.KEY_DISTANCE, distance);
position.set(Position.KEY_TOTAL_DISTANCE, totalDistance + distance);
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/EngineHoursHandler.java b/src/main/java/org/traccar/handler/EngineHoursHandler.java
index 621205b34..5aae6f673 100644
--- a/src/main/java/org/traccar/handler/EngineHoursHandler.java
+++ b/src/main/java/org/traccar/handler/EngineHoursHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,11 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import org.traccar.BaseDataHandler;
+import jakarta.inject.Inject;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
-public class EngineHoursHandler extends BaseDataHandler {
+public class EngineHoursHandler extends BasePositionHandler {
private final CacheManager cacheManager;
@@ -36,7 +30,7 @@ public class EngineHoursHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
if (!position.hasAttribute(Position.KEY_HOURS)) {
Position last = cacheManager.getPosition(position.getDeviceId());
if (last != null) {
@@ -49,7 +43,7 @@ public class EngineHoursHandler extends BaseDataHandler {
}
}
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java
index a15d3ffad..796c302fb 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 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,7 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
+import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
@@ -36,17 +34,12 @@ import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Order;
import org.traccar.storage.query.Request;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
import java.util.Date;
-@Singleton
-@ChannelHandler.Sharable
-public class FilterHandler extends ChannelInboundHandlerAdapter {
+public class FilterHandler extends BasePositionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(FilterHandler.class);
- private final boolean enabled;
private final boolean filterInvalid;
private final boolean filterZero;
private final boolean filterDuplicate;
@@ -71,7 +64,6 @@ public class FilterHandler extends ChannelInboundHandlerAdapter {
@Inject
public FilterHandler(
Config config, CacheManager cacheManager, Storage storage, StatisticsManager statisticsManager) {
- enabled = config.getBoolean(Keys.FILTER_ENABLE);
filterInvalid = config.getBoolean(Keys.FILTER_INVALID);
filterZero = config.getBoolean(Keys.FILTER_ZERO);
filterDuplicate = config.getBoolean(Keys.FILTER_DUPLICATE);
@@ -277,17 +269,8 @@ public class FilterHandler extends ChannelInboundHandlerAdapter {
}
@Override
- 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);
- }
+ public void handlePosition(Position position, Callback callback) {
+ callback.processed(filter(position));
}
}
diff --git a/src/main/java/org/traccar/handler/GeocoderHandler.java b/src/main/java/org/traccar/handler/GeocoderHandler.java
index e4f240a90..b84237856 100644
--- a/src/main/java/org/traccar/handler/GeocoderHandler.java
+++ b/src/main/java/org/traccar/handler/GeocoderHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,9 +15,6 @@
*/
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.config.Config;
@@ -26,8 +23,7 @@ import org.traccar.geocoder.Geocoder;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-@ChannelHandler.Sharable
-public class GeocoderHandler extends ChannelInboundHandlerAdapter {
+public class GeocoderHandler extends BasePositionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GeocoderHandler.class);
@@ -46,39 +42,38 @@ public class GeocoderHandler extends ChannelInboundHandlerAdapter {
}
@Override
- public void channelRead(final ChannelHandlerContext ctx, Object message) {
- if (message instanceof Position && !ignorePositions) {
- final Position position = (Position) message;
- if (processInvalidPositions || position.getValid()) {
- if (reuseDistance != 0) {
- Position lastPosition = cacheManager.getPosition(position.getDeviceId());
- if (lastPosition != null && lastPosition.getAddress() != null
- && position.getDouble(Position.KEY_DISTANCE) <= reuseDistance) {
- position.setAddress(lastPosition.getAddress());
- ctx.fireChannelRead(position);
- return;
- }
+ public void handlePosition(Position position, Callback callback) {
+ if (!ignorePositions) {
+ callback.processed(false);
+ }
+
+ if (processInvalidPositions || position.getValid()) {
+ if (reuseDistance != 0) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
+ if (lastPosition != null && lastPosition.getAddress() != null
+ && position.getDouble(Position.KEY_DISTANCE) <= reuseDistance) {
+ position.setAddress(lastPosition.getAddress());
+ callback.processed(false);
+ return;
}
+ }
- geocoder.getAddress(position.getLatitude(), position.getLongitude(),
- new Geocoder.ReverseGeocoderCallback() {
- @Override
- public void onSuccess(String address) {
- position.setAddress(address);
- ctx.fireChannelRead(position);
- }
+ geocoder.getAddress(position.getLatitude(), position.getLongitude(),
+ new Geocoder.ReverseGeocoderCallback() {
+ @Override
+ public void onSuccess(String address) {
+ position.setAddress(address);
+ callback.processed(false);
+ }
- @Override
- public void onFailure(Throwable e) {
- LOGGER.warn("Geocoding failed", e);
- ctx.fireChannelRead(position);
- }
- });
- } else {
- ctx.fireChannelRead(position);
- }
+ @Override
+ public void onFailure(Throwable e) {
+ LOGGER.warn("Geocoding failed", e);
+ callback.processed(false);
+ }
+ });
} else {
- ctx.fireChannelRead(message);
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/GeofenceHandler.java b/src/main/java/org/traccar/handler/GeofenceHandler.java
index 68bc6dbf0..8b363057a 100644
--- a/src/main/java/org/traccar/handler/GeofenceHandler.java
+++ b/src/main/java/org/traccar/handler/GeofenceHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2023 - 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.
@@ -15,20 +15,15 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import org.traccar.BaseDataHandler;
+import jakarta.inject.Inject;
import org.traccar.config.Config;
import org.traccar.helper.model.GeofenceUtil;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
import java.util.List;
-@Singleton
-@ChannelHandler.Sharable
-public class GeofenceHandler extends BaseDataHandler {
+public class GeofenceHandler extends BasePositionHandler {
private final Config config;
private final CacheManager cacheManager;
@@ -40,13 +35,13 @@ public class GeofenceHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
List<Long> geofenceIds = GeofenceUtil.getCurrentGeofences(config, cacheManager, position);
if (!geofenceIds.isEmpty()) {
position.setGeofenceIds(geofenceIds);
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/GeolocationHandler.java b/src/main/java/org/traccar/handler/GeolocationHandler.java
index a54ea03e3..c46bd3250 100644
--- a/src/main/java/org/traccar/handler/GeolocationHandler.java
+++ b/src/main/java/org/traccar/handler/GeolocationHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -15,9 +15,6 @@
*/
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.config.Config;
@@ -27,8 +24,7 @@ import org.traccar.geolocation.GeolocationProvider;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-@ChannelHandler.Sharable
-public class GeolocationHandler extends ChannelInboundHandlerAdapter {
+public class GeolocationHandler extends BasePositionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GeolocationHandler.class);
@@ -51,46 +47,41 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
}
@Override
- public void channelRead(final ChannelHandlerContext ctx, Object message) {
- if (message instanceof Position) {
- final Position position = (Position) message;
- if ((position.getOutdated() || processInvalidPositions && !position.getValid())
- && position.getNetwork() != null
- && (!requireWifi || position.getNetwork().getWifiAccessPoints() != null)) {
- if (reuse) {
- Position lastPosition = cacheManager.getPosition(position.getDeviceId());
- if (lastPosition != null && position.getNetwork().equals(lastPosition.getNetwork())) {
- updatePosition(
- position, lastPosition.getLatitude(), lastPosition.getLongitude(),
- lastPosition.getAccuracy());
- ctx.fireChannelRead(position);
- return;
- }
+ public void handlePosition(Position position, Callback callback) {
+ if ((position.getOutdated() || processInvalidPositions && !position.getValid())
+ && position.getNetwork() != null
+ && (!requireWifi || position.getNetwork().getWifiAccessPoints() != null)) {
+ if (reuse) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
+ if (lastPosition != null && position.getNetwork().equals(lastPosition.getNetwork())) {
+ updatePosition(
+ position, lastPosition.getLatitude(), lastPosition.getLongitude(),
+ lastPosition.getAccuracy());
+ callback.processed(false);
+ return;
}
+ }
- if (statisticsManager != null) {
- statisticsManager.registerGeolocationRequest();
- }
+ if (statisticsManager != null) {
+ statisticsManager.registerGeolocationRequest();
+ }
- geolocationProvider.getLocation(position.getNetwork(),
- new GeolocationProvider.LocationProviderCallback() {
- @Override
- public void onSuccess(double latitude, double longitude, double accuracy) {
- updatePosition(position, latitude, longitude, accuracy);
- ctx.fireChannelRead(position);
- }
+ geolocationProvider.getLocation(position.getNetwork(),
+ new GeolocationProvider.LocationProviderCallback() {
+ @Override
+ public void onSuccess(double latitude, double longitude, double accuracy) {
+ updatePosition(position, latitude, longitude, accuracy);
+ callback.processed(false);
+ }
- @Override
- public void onFailure(Throwable e) {
- LOGGER.warn("Geolocation network error", e);
- ctx.fireChannelRead(position);
- }
- });
- } else {
- ctx.fireChannelRead(position);
- }
+ @Override
+ public void onFailure(Throwable e) {
+ LOGGER.warn("Geolocation network error", e);
+ callback.processed(false);
+ }
+ });
} else {
- ctx.fireChannelRead(message);
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/HemisphereHandler.java b/src/main/java/org/traccar/handler/HemisphereHandler.java
index 294e449db..48929538f 100644
--- a/src/main/java/org/traccar/handler/HemisphereHandler.java
+++ b/src/main/java/org/traccar/handler/HemisphereHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,18 +15,12 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import org.traccar.BaseDataHandler;
+import jakarta.inject.Inject;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
-public class HemisphereHandler extends BaseDataHandler {
+public class HemisphereHandler extends BasePositionHandler {
private int latitudeFactor;
private int longitudeFactor;
@@ -52,14 +46,14 @@ public class HemisphereHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
if (latitudeFactor != 0) {
position.setLatitude(Math.abs(position.getLatitude()) * latitudeFactor);
}
if (longitudeFactor != 0) {
position.setLongitude(Math.abs(position.getLongitude()) * longitudeFactor);
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java
index 68a31a16a..804ffdc25 100644
--- a/src/main/java/org/traccar/handler/MotionHandler.java
+++ b/src/main/java/org/traccar/handler/MotionHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,13 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import org.traccar.BaseDataHandler;
+import jakarta.inject.Inject;
import org.traccar.config.Keys;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
-public class MotionHandler extends BaseDataHandler {
+public class MotionHandler extends BasePositionHandler {
private final CacheManager cacheManager;
@@ -38,13 +32,13 @@ public class MotionHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
if (!position.hasAttribute(Position.KEY_MOTION)) {
double threshold = AttributeUtil.lookup(
cacheManager, Keys.EVENT_MOTION_SPEED_THRESHOLD, position.getDeviceId());
position.set(Position.KEY_MOTION, position.getSpeed() > threshold);
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/OutdatedHandler.java b/src/main/java/org/traccar/handler/OutdatedHandler.java
new file mode 100644
index 000000000..536440f66
--- /dev/null
+++ b/src/main/java/org/traccar/handler/OutdatedHandler.java
@@ -0,0 +1,56 @@
+/*
+ * 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.handler;
+
+import jakarta.inject.Inject;
+import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+
+import java.util.Date;
+
+public class OutdatedHandler extends BasePositionHandler {
+
+ private final CacheManager cacheManager;
+
+ @Inject
+ public OutdatedHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
+ }
+
+ @Override
+ public void handlePosition(Position position, Callback callback) {
+ if (position.getOutdated()) {
+ Position last = cacheManager.getPosition(position.getDeviceId());
+ if (last != null) {
+ position.setFixTime(last.getFixTime());
+ position.setValid(last.getValid());
+ position.setLatitude(last.getLatitude());
+ position.setLongitude(last.getLongitude());
+ position.setAltitude(last.getAltitude());
+ position.setSpeed(last.getSpeed());
+ position.setCourse(last.getCourse());
+ position.setAccuracy(last.getAccuracy());
+ } else {
+ position.setFixTime(new Date(315964819000L)); // gps epoch 1980-01-06
+ }
+ if (position.getDeviceTime() == null) {
+ position.setDeviceTime(position.getServerTime());
+ }
+ }
+ callback.processed(false);
+ }
+
+}
diff --git a/src/main/java/org/traccar/PositionForwardingHandler.java b/src/main/java/org/traccar/handler/PositionForwardingHandler.java
index a79b01367..8512d4552 100644
--- a/src/main/java/org/traccar/PositionForwardingHandler.java
+++ b/src/main/java/org/traccar/handler/PositionForwardingHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
+import jakarta.annotation.Nullable;
+import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
@@ -30,15 +31,10 @@ import org.traccar.model.Device;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.annotation.Nullable;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-@Singleton
-@ChannelHandler.Sharable
-public class PositionForwardingHandler extends BaseDataHandler {
+public class PositionForwardingHandler extends BasePositionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(PositionForwardingHandler.class);
@@ -128,14 +124,14 @@ public class PositionForwardingHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
+ public void handlePosition(Position position, Callback callback) {
if (positionForwarder != null) {
PositionData positionData = new PositionData();
positionData.setPosition(position);
positionData.setDevice(cacheManager.getObject(Device.class, position.getDeviceId()));
new AsyncRequestAndCallback(positionData).send();
}
- return position;
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/PostProcessHandler.java b/src/main/java/org/traccar/handler/PostProcessHandler.java
new file mode 100644
index 000000000..5b1b2ef86
--- /dev/null
+++ b/src/main/java/org/traccar/handler/PostProcessHandler.java
@@ -0,0 +1,67 @@
+/*
+ * 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.handler;
+
+import jakarta.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+import org.traccar.session.ConnectionManager;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+public class PostProcessHandler extends BasePositionHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PostProcessHandler.class);
+
+ private final CacheManager cacheManager;
+ private final Storage storage;
+ private final ConnectionManager connectionManager;
+
+ @Inject
+ public PostProcessHandler(CacheManager cacheManager, Storage storage, ConnectionManager connectionManager) {
+ this.cacheManager = cacheManager;
+ this.storage = storage;
+ this.connectionManager = connectionManager;
+ }
+
+ @Override
+ public void handlePosition(Position position, Callback callback) {
+ try {
+ if (PositionUtil.isLatest(cacheManager, position)) {
+ Device updatedDevice = new Device();
+ updatedDevice.setId(position.getDeviceId());
+ updatedDevice.setPositionId(position.getId());
+ storage.updateObject(updatedDevice, new Request(
+ new Columns.Include("positionId"),
+ new Condition.Equals("id", updatedDevice.getId())));
+
+ cacheManager.updatePosition(position);
+ connectionManager.updatePosition(true, position);
+ }
+ } catch (StorageException error) {
+ LOGGER.warn("Failed to update device", error);
+ }
+ callback.processed(false);
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
index 6edb6e912..4c0922d01 100644
--- a/src/main/java/org/traccar/handler/SpeedLimitHandler.java
+++ b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 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.
@@ -15,20 +15,13 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
+import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.model.Position;
import org.traccar.speedlimit.SpeedLimitProvider;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
-public class SpeedLimitHandler extends ChannelInboundHandlerAdapter {
+public class SpeedLimitHandler extends BasePositionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(SpeedLimitHandler.class);
@@ -40,26 +33,22 @@ public class SpeedLimitHandler extends ChannelInboundHandlerAdapter {
}
@Override
- public void channelRead(final ChannelHandlerContext ctx, Object message) {
- if (message instanceof Position) {
- final Position position = (Position) message;
- speedLimitProvider.getSpeedLimit(position.getLatitude(), position.getLongitude(),
- new SpeedLimitProvider.SpeedLimitProviderCallback() {
- @Override
- public void onSuccess(double speedLimit) {
- position.set(Position.KEY_SPEED_LIMIT, speedLimit);
- ctx.fireChannelRead(position);
- }
-
- @Override
- public void onFailure(Throwable e) {
- LOGGER.warn("Speed limit provider failed", e);
- ctx.fireChannelRead(position);
- }
- });
- } else {
- ctx.fireChannelRead(message);
- }
+ public void handlePosition(Position position, Callback callback) {
+
+ speedLimitProvider.getSpeedLimit(position.getLatitude(), position.getLongitude(),
+ new SpeedLimitProvider.SpeedLimitProviderCallback() {
+ @Override
+ public void onSuccess(double speedLimit) {
+ position.set(Position.KEY_SPEED_LIMIT, speedLimit);
+ callback.processed(false);
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ LOGGER.warn("Speed limit provider failed", e);
+ callback.processed(false);
+ }
+ });
}
}
diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java
index 3c3e17450..f6c67bb76 100644
--- a/src/main/java/org/traccar/handler/TimeHandler.java
+++ b/src/main/java/org/traccar/handler/TimeHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 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.
@@ -15,36 +15,23 @@
*/
package org.traccar.handler;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import org.traccar.BaseProtocolDecoder;
+import jakarta.inject.Inject;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
-@Singleton
-@ChannelHandler.Sharable
-public class TimeHandler extends ChannelInboundHandlerAdapter {
+public class TimeHandler extends BasePositionHandler {
- private final boolean enabled;
private final boolean useServerTime;
private final Set<String> protocols;
@Inject
public TimeHandler(Config config) {
- enabled = config.hasKey(Keys.TIME_OVERRIDE);
- if (enabled) {
- useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime");
- } else {
- useServerTime = false;
- }
+ useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime");
String protocolList = config.getString(Keys.TIME_PROTOCOLS);
if (protocolList != null) {
protocols = new HashSet<>(Arrays.asList(protocolList.split("[, ]")));
@@ -54,21 +41,17 @@ public class TimeHandler extends ChannelInboundHandlerAdapter {
}
@Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) {
-
- if (enabled && msg instanceof Position && (protocols == null
- || protocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName()))) {
+ public void handlePosition(Position position, Callback callback) {
- Position position = (Position) msg;
+ if (protocols == null || protocols.contains(position.getProtocol())) {
if (useServerTime) {
position.setDeviceTime(position.getServerTime());
position.setFixTime(position.getServerTime());
} else {
position.setFixTime(position.getDeviceTime());
}
-
}
- ctx.fireChannelRead(msg);
+ callback.processed(false);
}
}
diff --git a/src/main/java/org/traccar/handler/events/AlertEventHandler.java b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
index 531a0f957..ca580b60d 100644
--- a/src/main/java/org/traccar/handler/events/AlertEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,21 +15,13 @@
*/
package org.traccar.handler.events;
-import java.util.Collections;
-import java.util.Map;
-
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
public class AlertEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
@@ -42,7 +34,7 @@ public class AlertEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
Object alarm = position.getAttributes().get(Position.KEY_ALARM);
if (alarm != null) {
boolean ignoreAlert = false;
@@ -55,10 +47,9 @@ public class AlertEventHandler extends BaseEventHandler {
if (!ignoreAlert) {
Event event = new Event(Event.TYPE_ALARM, position);
event.set(Position.KEY_ALARM, (String) alarm);
- return Collections.singletonMap(event, position);
+ callback.eventDetected(event);
}
}
- return null;
}
}
diff --git a/src/main/java/org/traccar/handler/events/BaseEventHandler.java b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
index 4a4fb40ff..009c83145 100644
--- a/src/main/java/org/traccar/handler/events/BaseEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,33 +15,17 @@
*/
package org.traccar.handler.events;
-import java.util.Map;
-
-import org.traccar.BaseDataHandler;
-import org.traccar.database.NotificationManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import jakarta.inject.Inject;
-
-public abstract class BaseEventHandler extends BaseDataHandler {
-
- private NotificationManager notificationManager;
+public abstract class BaseEventHandler {
- @Inject
- public void setNotificationManager(NotificationManager notificationManager) {
- this.notificationManager = notificationManager;
+ public interface Callback {
+ void eventDetected(Event event);
}
- @Override
- protected Position handlePosition(Position position) {
- Map<Event, Position> events = analyzePosition(position);
- if (events != null && !events.isEmpty()) {
- notificationManager.updateEvents(events);
- }
- return position;
- }
-
- protected abstract Map<Event, Position> analyzePosition(Position position);
-
+ /**
+ * Event handlers should be processed synchronously.
+ */
+ public abstract void analyzePosition(Position position, Callback callback);
}
diff --git a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
index 08ae35fcd..d654e18ce 100644
--- a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 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.
@@ -15,7 +15,7 @@
*/
package org.traccar.handler.events;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.UnitsConverter;
@@ -23,13 +23,6 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.util.Collections;
-import java.util.Map;
-
-@Singleton
-@ChannelHandler.Sharable
public class BehaviorEventHandler extends BaseEventHandler {
private final double accelerationThreshold;
@@ -45,7 +38,7 @@ public class BehaviorEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
Position lastPosition = cacheManager.getPosition(position.getDeviceId());
if (lastPosition != null && position.getFixTime().equals(lastPosition.getFixTime())) {
@@ -54,14 +47,13 @@ public class BehaviorEventHandler extends BaseEventHandler {
if (accelerationThreshold != 0 && acceleration >= accelerationThreshold) {
Event event = new Event(Event.TYPE_ALARM, position);
event.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
- return Collections.singletonMap(event, position);
+ callback.eventDetected(event);
} else if (brakingThreshold != 0 && acceleration <= -brakingThreshold) {
Event event = new Event(Event.TYPE_ALARM, position);
event.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
- return Collections.singletonMap(event, position);
+ callback.eventDetected(event);
}
}
- return null;
}
}
diff --git a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
index b70f8f33b..b98807b23 100644
--- a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,18 +15,10 @@
*/
package org.traccar.handler.events;
-import java.util.Collections;
-import java.util.Map;
-
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
public class CommandResultEventHandler extends BaseEventHandler {
@Inject
@@ -34,14 +26,13 @@ public class CommandResultEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
Object commandResult = position.getAttributes().get(Position.KEY_RESULT);
if (commandResult != null) {
Event event = new Event(Event.TYPE_COMMAND_RESULT, position);
event.set(Position.KEY_RESULT, (String) commandResult);
- return Collections.singletonMap(event, position);
+ callback.eventDetected(event);
}
- return null;
}
}
diff --git a/src/main/java/org/traccar/handler/events/DriverEventHandler.java b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
index b68327983..31f8d2b4b 100644
--- a/src/main/java/org/traccar/handler/events/DriverEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,12 @@
*/
package org.traccar.handler.events;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.util.Collections;
-import java.util.Map;
-
-@Singleton
-@ChannelHandler.Sharable
public class DriverEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
@@ -39,9 +32,9 @@ public class DriverEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
if (!PositionUtil.isLatest(cacheManager, position)) {
- return null;
+ return;
}
String driverUniqueId = position.getString(Position.KEY_DRIVER_UNIQUE_ID);
if (driverUniqueId != null) {
@@ -53,10 +46,9 @@ public class DriverEventHandler extends BaseEventHandler {
if (!driverUniqueId.equals(oldDriverUniqueId)) {
Event event = new Event(Event.TYPE_DRIVER_CHANGED, position);
event.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId);
- return Collections.singletonMap(event, position);
+ callback.eventDetected(event);
}
}
- return null;
}
}
diff --git a/src/main/java/org/traccar/handler/events/FuelEventHandler.java b/src/main/java/org/traccar/handler/events/FuelEventHandler.java
index e5085ecc2..c5675f51d 100644
--- a/src/main/java/org/traccar/handler/events/FuelEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/FuelEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,7 +15,7 @@
*/
package org.traccar.handler.events;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.config.Keys;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.helper.model.PositionUtil;
@@ -24,12 +24,6 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.util.Map;
-
-@Singleton
-@ChannelHandler.Sharable
public class FuelEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
@@ -40,14 +34,14 @@ public class FuelEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
Device device = cacheManager.getObject(Device.class, position.getDeviceId());
if (device == null) {
- return null;
+ return;
}
if (!PositionUtil.isLatest(cacheManager, position)) {
- return null;
+ return;
}
if (position.hasAttribute(Position.KEY_FUEL_LEVEL)) {
@@ -61,19 +55,17 @@ public class FuelEventHandler extends BaseEventHandler {
double threshold = AttributeUtil.lookup(
cacheManager, Keys.EVENT_FUEL_INCREASE_THRESHOLD, position.getDeviceId());
if (threshold > 0 && change >= threshold) {
- return Map.of(new Event(Event.TYPE_DEVICE_FUEL_INCREASE, position), position);
+ callback.eventDetected(new Event(Event.TYPE_DEVICE_FUEL_INCREASE, position));
}
} else if (change < 0) {
double threshold = AttributeUtil.lookup(
cacheManager, Keys.EVENT_FUEL_DROP_THRESHOLD, position.getDeviceId());
if (threshold > 0 && Math.abs(change) >= threshold) {
- return Map.of(new Event(Event.TYPE_DEVICE_FUEL_DROP, position), position);
+ callback.eventDetected(new Event(Event.TYPE_DEVICE_FUEL_DROP, position));
}
}
}
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
index dbe2b8118..c8ecfb1ed 100644
--- a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,7 +15,7 @@
*/
package org.traccar.handler.events;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Calendar;
import org.traccar.model.Event;
@@ -23,15 +23,9 @@ import org.traccar.model.Geofence;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-@Singleton
-@ChannelHandler.Sharable
public class GeofenceEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
@@ -42,9 +36,9 @@ public class GeofenceEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
if (!PositionUtil.isLatest(cacheManager, position)) {
- return null;
+ return;
}
List<Long> oldGeofences = new ArrayList<>();
@@ -60,7 +54,6 @@ public class GeofenceEventHandler extends BaseEventHandler {
oldGeofences.removeAll(position.getGeofenceIds());
}
- Map<Event, Position> events = new HashMap<>();
for (long geofenceId : oldGeofences) {
Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId);
if (geofence != null) {
@@ -69,7 +62,7 @@ public class GeofenceEventHandler extends BaseEventHandler {
if (calendar == null || calendar.checkMoment(position.getFixTime())) {
Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position);
event.setGeofenceId(geofenceId);
- events.put(event, position);
+ callback.eventDetected(event);
}
}
}
@@ -79,10 +72,8 @@ public class GeofenceEventHandler extends BaseEventHandler {
if (calendar == null || calendar.checkMoment(position.getFixTime())) {
Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position);
event.setGeofenceId(geofenceId);
- events.put(event, position);
+ callback.eventDetected(event);
}
}
- return events;
}
-
}
diff --git a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
index ba4159a42..bbf9fadd1 100644
--- a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,21 +16,13 @@
*/
package org.traccar.handler.events;
-import java.util.Collections;
-import java.util.Map;
-
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
public class IgnitionEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
@@ -41,14 +33,12 @@ public class IgnitionEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
Device device = cacheManager.getObject(Device.class, position.getDeviceId());
if (device == null || !PositionUtil.isLatest(cacheManager, position)) {
- return null;
+ return;
}
- Map<Event, Position> result = null;
-
if (position.hasAttribute(Position.KEY_IGNITION)) {
boolean ignition = position.getBoolean(Position.KEY_IGNITION);
@@ -57,15 +47,12 @@ public class IgnitionEventHandler extends BaseEventHandler {
boolean oldIgnition = lastPosition.getBoolean(Position.KEY_IGNITION);
if (ignition && !oldIgnition) {
- result = Collections.singletonMap(
- new Event(Event.TYPE_IGNITION_ON, position), position);
+ callback.eventDetected(new Event(Event.TYPE_IGNITION_ON, position));
} else if (!ignition && oldIgnition) {
- result = Collections.singletonMap(
- new Event(Event.TYPE_IGNITION_OFF, position), position);
+ callback.eventDetected(new Event(Event.TYPE_IGNITION_OFF, position));
}
}
}
- return result;
}
}
diff --git a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
index 2fa2e8869..573ad4ad6 100644
--- a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
@@ -16,20 +16,12 @@
*/
package org.traccar.handler.events;
-import java.util.HashMap;
-import java.util.Map;
-
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.model.Event;
import org.traccar.model.Maintenance;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ChannelHandler.Sharable
public class MaintenanceEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
@@ -40,13 +32,12 @@ public class MaintenanceEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
Position lastPosition = cacheManager.getPosition(position.getDeviceId());
if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) < 0) {
- return null;
+ return;
}
- Map<Event, Position> events = new HashMap<>();
for (Maintenance maintenance : cacheManager.getDeviceObjects(position.getDeviceId(), Maintenance.class)) {
if (maintenance.getPeriod() != 0) {
double oldValue = getValue(lastPosition, maintenance.getType());
@@ -58,13 +49,11 @@ public class MaintenanceEventHandler extends BaseEventHandler {
Event event = new Event(Event.TYPE_MAINTENANCE, position);
event.setMaintenanceId(maintenance.getId());
event.set(maintenance.getType(), newValue);
- events.put(event, position);
+ callback.eventDetected(event);
}
}
}
}
-
- return events;
}
private double getValue(Position position, String type) {
diff --git a/src/main/java/org/traccar/handler/events/MediaEventHandler.java b/src/main/java/org/traccar/handler/events/MediaEventHandler.java
index 52d8e6961..2745296c4 100644
--- a/src/main/java/org/traccar/handler/events/MediaEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/MediaEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2022 - 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.
@@ -15,18 +15,12 @@
*/
package org.traccar.handler.events;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.util.Map;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
-@Singleton
-@ChannelHandler.Sharable
public class MediaEventHandler extends BaseEventHandler {
@Inject
@@ -34,8 +28,8 @@ public class MediaEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
- return Stream.of(Position.KEY_IMAGE, Position.KEY_VIDEO, Position.KEY_AUDIO)
+ public void analyzePosition(Position position, Callback callback) {
+ Stream.of(Position.KEY_IMAGE, Position.KEY_VIDEO, Position.KEY_AUDIO)
.filter(position::hasAttribute)
.map(type -> {
Event event = new Event(Event.TYPE_MEDIA, position);
@@ -43,7 +37,7 @@ public class MediaEventHandler extends BaseEventHandler {
event.set("file", position.getString(type));
return event;
})
- .collect(Collectors.toMap(event -> event, event -> position));
+ .forEach(callback::eventDetected);
}
}
diff --git a/src/main/java/org/traccar/handler/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
index 15902d6d4..41d68985b 100644
--- a/src/main/java/org/traccar/handler/events/MotionEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,13 @@
*/
package org.traccar.handler.events;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Keys;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Device;
-import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.reports.common.TripsConfig;
import org.traccar.session.cache.CacheManager;
@@ -35,13 +34,6 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.util.Collections;
-import java.util.Map;
-
-@Singleton
-@ChannelHandler.Sharable
public class MotionEventHandler extends BaseEventHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MotionEventHandler.class);
@@ -56,17 +48,17 @@ public class MotionEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
long deviceId = position.getDeviceId();
Device device = cacheManager.getObject(Device.class, deviceId);
if (device == null || !PositionUtil.isLatest(cacheManager, position)) {
- return null;
+ return;
}
boolean processInvalid = AttributeUtil.lookup(
cacheManager, Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS, deviceId);
if (!processInvalid && !position.getValid()) {
- return null;
+ return;
}
TripsConfig tripsConfig = new TripsConfig(new AttributeUtil.CacheProvider(cacheManager, deviceId));
@@ -82,7 +74,9 @@ public class MotionEventHandler extends BaseEventHandler {
LOGGER.warn("Update device motion error", e);
}
}
- return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null;
+ if (state.getEvent() != null) {
+ callback.eventDetected(state.getEvent());
+ }
}
}
diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
index 3bb5f713c..9598581e6 100644
--- a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,7 @@
*/
package org.traccar.handler.events;
-import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
@@ -24,7 +24,6 @@ import org.traccar.config.Keys;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Device;
-import org.traccar.model.Event;
import org.traccar.model.Geofence;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
@@ -36,13 +35,6 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.util.Collections;
-import java.util.Map;
-
-@Singleton
-@ChannelHandler.Sharable
public class OverspeedEventHandler extends BaseEventHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(OverspeedEventHandler.class);
@@ -55,8 +47,7 @@ public class OverspeedEventHandler extends BaseEventHandler {
private final double multiplier;
@Inject
- public OverspeedEventHandler(
- Config config, CacheManager cacheManager, Storage storage) {
+ public OverspeedEventHandler(Config config, CacheManager cacheManager, Storage storage) {
this.cacheManager = cacheManager;
this.storage = storage;
minimalDuration = config.getLong(Keys.EVENT_OVERSPEED_MINIMAL_DURATION) * 1000;
@@ -65,15 +56,15 @@ public class OverspeedEventHandler extends BaseEventHandler {
}
@Override
- protected Map<Event, Position> analyzePosition(Position position) {
+ public void analyzePosition(Position position, Callback callback) {
long deviceId = position.getDeviceId();
Device device = cacheManager.getObject(Device.class, position.getDeviceId());
if (device == null) {
- return null;
+ return;
}
if (!PositionUtil.isLatest(cacheManager, position) || !position.getValid()) {
- return null;
+ return;
}
double speedLimit = AttributeUtil.lookup(cacheManager, Keys.EVENT_OVERSPEED_LIMIT, deviceId);
@@ -105,7 +96,7 @@ public class OverspeedEventHandler extends BaseEventHandler {
}
if (speedLimit == 0) {
- return null;
+ return;
}
OverspeedState state = OverspeedState.fromDevice(device);
@@ -120,7 +111,9 @@ public class OverspeedEventHandler extends BaseEventHandler {
LOGGER.warn("Update device overspeed error", e);
}
}
- return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null;
+ if (state.getEvent() != null) {
+ callback.eventDetected(state.getEvent());
+ }
}
}
diff --git a/src/main/java/org/traccar/handler/AcknowledgementHandler.java b/src/main/java/org/traccar/handler/network/AcknowledgementHandler.java
index 4c1085998..e87f5d34c 100644
--- a/src/main/java/org/traccar/handler/AcknowledgementHandler.java
+++ b/src/main/java/org/traccar/handler/network/AcknowledgementHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.handler;
+package org.traccar.handler.network;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
diff --git a/src/main/java/org/traccar/handler/network/MainEventHandler.java b/src/main/java/org/traccar/handler/network/MainEventHandler.java
new file mode 100644
index 000000000..f60004126
--- /dev/null
+++ b/src/main/java/org/traccar/handler/network/MainEventHandler.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012 - 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.handler.network;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.timeout.IdleStateEvent;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.BasePipelineFactory;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.NetworkUtil;
+import org.traccar.session.ConnectionManager;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@Singleton
+@ChannelHandler.Sharable
+public class MainEventHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MainEventHandler.class);
+
+ private final ConnectionManager connectionManager;
+ private final Set<String> connectionlessProtocols = new HashSet<>();
+
+ @Inject
+ public MainEventHandler(Config config, ConnectionManager connectionManager) {
+ this.connectionManager = connectionManager;
+ String connectionlessProtocolList = config.getString(Keys.STATUS_IGNORE_OFFLINE);
+ if (connectionlessProtocolList != null) {
+ connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split("[, ]")));
+ }
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ if (!(ctx.channel() instanceof DatagramChannel)) {
+ LOGGER.info("[{}] connected", NetworkUtil.session(ctx.channel()));
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) {
+ LOGGER.info("[{}] disconnected", NetworkUtil.session(ctx.channel()));
+ closeChannel(ctx.channel());
+
+ boolean supportsOffline = BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null
+ && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName());
+ connectionManager.deviceDisconnected(ctx.channel(), supportsOffline);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ while (cause.getCause() != null && cause.getCause() != cause) {
+ cause = cause.getCause();
+ }
+ LOGGER.info("[{}] error", NetworkUtil.session(ctx.channel()), cause);
+ closeChannel(ctx.channel());
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
+ if (evt instanceof IdleStateEvent) {
+ LOGGER.info("[{}] timed out", NetworkUtil.session(ctx.channel()));
+ closeChannel(ctx.channel());
+ }
+ }
+
+ private void closeChannel(Channel channel) {
+ if (!(channel instanceof DatagramChannel)) {
+ channel.close();
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/NetworkForwarderHandler.java b/src/main/java/org/traccar/handler/network/NetworkForwarderHandler.java
index 470e175ca..5f07ce355 100644
--- a/src/main/java/org/traccar/handler/NetworkForwarderHandler.java
+++ b/src/main/java/org/traccar/handler/network/NetworkForwarderHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.handler;
+package org.traccar.handler.network;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
diff --git a/src/main/java/org/traccar/handler/NetworkMessageHandler.java b/src/main/java/org/traccar/handler/network/NetworkMessageHandler.java
index b1d926bfa..c2fb9016a 100644
--- a/src/main/java/org/traccar/handler/NetworkMessageHandler.java
+++ b/src/main/java/org/traccar/handler/network/NetworkMessageHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.handler;
+package org.traccar.handler.network;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
diff --git a/src/main/java/org/traccar/handler/OpenChannelHandler.java b/src/main/java/org/traccar/handler/network/OpenChannelHandler.java
index e416f35ae..21aaae676 100644
--- a/src/main/java/org/traccar/handler/OpenChannelHandler.java
+++ b/src/main/java/org/traccar/handler/network/OpenChannelHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.handler;
+package org.traccar.handler.network;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
diff --git a/src/main/java/org/traccar/handler/RemoteAddressHandler.java b/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java
index 61ada5b91..c52bb2be1 100644
--- a/src/main/java/org/traccar/handler/RemoteAddressHandler.java
+++ b/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.handler;
+package org.traccar.handler.network;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
import java.net.InetSocketAddress;
@Singleton
diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/network/StandardLoggingHandler.java
index 513602dd8..dae93655d 100644
--- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java
+++ b/src/main/java/org/traccar/handler/network/StandardLoggingHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.handler;
+package org.traccar.handler.network;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
diff --git a/src/main/java/org/traccar/helper/PositionLogger.java b/src/main/java/org/traccar/helper/PositionLogger.java
new file mode 100644
index 000000000..9f149edf4
--- /dev/null
+++ b/src/main/java/org/traccar/helper/PositionLogger.java
@@ -0,0 +1,94 @@
+/*
+ * 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.helper;
+
+import io.netty.channel.ChannelHandlerContext;
+import jakarta.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class PositionLogger {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PositionLogger.class);
+
+ private final CacheManager cacheManager;
+ private final Set<String> logAttributes = new LinkedHashSet<>();
+
+ @Inject
+ public PositionLogger(Config config, CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
+ logAttributes.addAll(Arrays.asList(config.getString(Keys.LOGGER_ATTRIBUTES).split("[, ]")));
+ }
+
+ public void log(ChannelHandlerContext context, Position position) {
+ Device device = cacheManager.getObject(Device.class, position.getDeviceId());
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("[").append(NetworkUtil.session(context.channel())).append("] ");
+ builder.append("id: ").append(device.getUniqueId());
+ for (String attribute : logAttributes) {
+ switch (attribute) {
+ case "time":
+ builder.append(", time: ").append(DateUtil.formatDate(position.getFixTime(), false));
+ break;
+ case "position":
+ builder.append(", lat: ").append(String.format("%.5f", position.getLatitude()));
+ builder.append(", lon: ").append(String.format("%.5f", position.getLongitude()));
+ break;
+ case "speed":
+ if (position.getSpeed() > 0) {
+ builder.append(", speed: ").append(String.format("%.1f", position.getSpeed()));
+ }
+ break;
+ case "course":
+ builder.append(", course: ").append(String.format("%.1f", position.getCourse()));
+ break;
+ case "accuracy":
+ if (position.getAccuracy() > 0) {
+ builder.append(", accuracy: ").append(String.format("%.1f", position.getAccuracy()));
+ }
+ break;
+ case "outdated":
+ if (position.getOutdated()) {
+ builder.append(", outdated");
+ }
+ break;
+ case "invalid":
+ if (!position.getValid()) {
+ builder.append(", invalid");
+ }
+ break;
+ default:
+ Object value = position.getAttributes().get(attribute);
+ if (value != null) {
+ builder.append(", ").append(attribute).append(": ").append(value);
+ }
+ break;
+ }
+ }
+ LOGGER.info(builder.toString());
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java
index a3088a613..c2da9be79 100644
--- a/src/main/java/org/traccar/model/Device.java
+++ b/src/main/java/org/traccar/model/Device.java
@@ -53,7 +53,7 @@ public class Device extends GroupedModel implements Disableable, Schedulable {
}
public void setUniqueId(String uniqueId) {
- if (uniqueId.contains("../") || uniqueId.contains("..\\")) {
+ if (uniqueId.contains("..")) {
throw new IllegalArgumentException("Invalid unique id");
}
this.uniqueId = uniqueId.trim();
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
index 8896dcfb0..834f4f16c 100644
--- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -325,6 +325,9 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
case "SA":
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
+ case "MT":
+ position.set(Position.KEY_MOTION, buf.readUnsignedByte() > 0);
+ break;
case "MV":
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
break;
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 7a8d67cf9..775e98401 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -41,12 +41,54 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
+ private static final Map<String, String> PROTOCOL_MODELS = Map.ofEntries(
+ Map.entry("02", "GL200"),
+ Map.entry("04", "GV200"),
+ Map.entry("06", "GV300"),
+ Map.entry("08", "GMT100"),
+ Map.entry("09", "GV50P"),
+ Map.entry("0F", "GV55"),
+ Map.entry("10", "GV55 LITE"),
+ Map.entry("11", "GL500"),
+ Map.entry("1A", "GL300"),
+ Map.entry("1F", "GV500"),
+ Map.entry("25", "GV300"),
+ Map.entry("27", "GV300W"),
+ Map.entry("28", "GL300VC"),
+ Map.entry("2C", "GL300W"),
+ Map.entry("2F", "GV55"),
+ Map.entry("30", "GL300"),
+ Map.entry("31", "GV65"),
+ Map.entry("35", "GV200"),
+ Map.entry("36", "GV500"),
+ Map.entry("3F", "GMT100"),
+ Map.entry("40", "GL500"),
+ Map.entry("41", "GV75W"),
+ Map.entry("42", "GT501"),
+ Map.entry("44", "GL530"),
+ Map.entry("45", "GB100"),
+ Map.entry("50", "GV55W"),
+ Map.entry("52", "GL50"),
+ Map.entry("55", "GL50B"),
+ Map.entry("5E", "GV500MAP"),
+ Map.entry("6E", "GV310LAU"),
+ Map.entry("BD", "CV200"),
+ Map.entry("C2", "GV600M"),
+ Map.entry("DC", "GV600MG"),
+ Map.entry("DE", "GL500M"),
+ Map.entry("F1", "GV350M"),
+ Map.entry("F8", "GV800W"),
+ Map.entry("FC", "GV600W"),
+ Map.entry("802004", "GV58LAU"),
+ Map.entry("802005", "GV355CEU"));
+
private boolean ignoreFixTime;
private final DateFormat dateFormat;
@@ -62,9 +104,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName()));
}
- private String getDeviceModel(DeviceSession deviceSession, String value) {
- String model = value.isEmpty() ? getDeviceModel(deviceSession) : value;
- return model != null ? model.toUpperCase() : "";
+ private String getDeviceModel(DeviceSession deviceSession, String protocolVersion) {
+ String declaredModel = getDeviceModel(deviceSession);
+ if (declaredModel != null) {
+ return declaredModel.toUpperCase();
+ }
+ String versionPrefix;
+ if (protocolVersion.length() > 6) {
+ versionPrefix = protocolVersion.substring(0, 6);
+ } else {
+ versionPrefix = protocolVersion.substring(0, 2);
+ }
+ String model = PROTOCOL_MODELS.get(versionPrefix);
+ return model != null ? model : "";
}
private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
@@ -122,12 +174,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN_INF = new PatternBuilder()
.text("+").expression("(?:RESP|BUFF):GTINF,")
- .expression("(?:.{6}|.{10})?,") // protocol version
+ .expression("(.{6}|.{10})?,") // protocol version
.number("(d{15}|x{14}),") // imei
.expression("(?:[0-9A-Z]{17},)?") // vin
.expression("(?:[^,]+)?,") // device name
.number("(xx),") // state
- .expression("(?:[0-9Ff]{20})?,") // iccid
+ .expression("([0-9Ff]{20})?,") // iccid
.number("(d{1,2}),") // rssi
.number("d{1,2},")
.expression("[01]{1,2},") // external power
@@ -148,6 +200,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.expression("(?:[01])?,").optional() // pin15 mode
.number("(d+)?,") // adc1
.number("(d+)?,").optional() // adc2
+ .number("(d+)?,").optional() // adc3
.number("(xx)?,") // digital input
.number("(xx)?,") // digital output
.number("[-+]dddd,") // timezone
@@ -163,10 +216,17 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
Parser parser = new Parser(PATTERN_INF, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
+ if (!parser.matches()) {
return null;
}
+ String protocolVersion = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
switch (parser.nextHexInt()) {
case 0x16:
@@ -197,9 +257,15 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
break;
}
+ position.set(Position.KEY_ICCID, parser.next());
position.set(Position.KEY_RSSI, parser.nextInt());
- parser.next(); // odometer or external power
+ String model = getDeviceModel(deviceSession, protocolVersion);
+ if (model.equals("GV310LAU")) {
+ position.set(Position.KEY_POWER, parser.nextDouble() / 1000);
+ } else {
+ parser.next(); // odometer or external power
+ }
position.set(Position.KEY_BATTERY, parser.nextDouble());
position.set(Position.KEY_CHARGE, parser.nextInt() == 1);
@@ -210,6 +276,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 1, parser.next());
position.set(Position.PREFIX_ADC + 2, parser.next());
+ if (model.equals("GV310LAU")) {
+ position.set(Position.PREFIX_ADC + 3, parser.next());
+ }
position.set(Position.KEY_INPUT, parser.next());
position.set(Position.KEY_OUTPUT, parser.next());
@@ -455,7 +524,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException {
int index = 0;
index += 1; // header
- index += 1; // protocol version
+ String protocolVersion = v[index++]; // protocol version
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]);
if (deviceSession == null) {
return null;
@@ -464,7 +533,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- String model = getDeviceModel(deviceSession, v[index++]);
+ String model = getDeviceModel(deviceSession, protocolVersion);
+ index += 1; // device name
index += 1; // report type
index += 1; // can bus state
long reportMask = Long.parseLong(v[index++], 16);
@@ -872,13 +942,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException {
int index = 0;
index += 1; // header
- index += 1; // protocol version
+ String protocolVersion = v[index++]; // protocol version
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]);
if (deviceSession == null) {
return null;
}
- String model = getDeviceModel(deviceSession, v[index++]);
+ String model = getDeviceModel(deviceSession, protocolVersion);
+ index += 1; // device name
long mask = Long.parseLong(v[index++], 16);
Double power = v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001;
index += 1; // report type
@@ -960,7 +1031,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN_IGN = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTIG[NF],")
+ .text("+").expression("(?:RESP|BUFF):GT[IV]G[NF],")
.expression("(?:.{6}|.{10})?,") // protocol version
.number("(d{15}|x{14}),") // imei
.expression("[^,]*,") // device name
@@ -984,7 +1055,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
decodeLocation(position, parser);
- position.set(Position.KEY_IGNITION, sentence.contains("IGN"));
+ position.set(Position.KEY_IGNITION, sentence.contains("GN"));
position.set(Position.KEY_HOURS, parseHours(parser.next()));
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
@@ -1606,6 +1677,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
break;
case "IGN":
case "IGF":
+ case "VGN":
+ case "VGF":
result = decodeIgn(channel, remoteAddress, sentence);
break;
case "LSW":
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index 2186fb91f..648c5fb42 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -979,6 +979,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
case 0x0103:
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedInt() * 0.01);
break;
+ case 0x0111:
+ position.set("fuelTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0x012E:
+ position.set("oilLevel", buf.readUnsignedShort() * 0.1);
+ break;
case 0x052A:
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.01);
break;
@@ -1113,6 +1119,30 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
}
getLastLocation(position, time);
break;
+ case 0x15:
+ int event = buf.readInt();
+ switch (event) {
+ case 51:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 52:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 53:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ case 54:
+ position.set(Position.KEY_ALARM, Position.ALARM_LANE_CHANGE);
+ break;
+ case 56:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ break;
+ default:
+ position.set(Position.KEY_EVENT, event);
+ break;
+ }
+ getLastLocation(position, time);
+ break;
default:
return null;
}