aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar')
-rw-r--r--src/main/java/org/traccar/api/MediaFilter.java11
-rw-r--r--src/main/java/org/traccar/config/Keys.java19
-rw-r--r--src/main/java/org/traccar/handler/MotionHandler.java16
-rw-r--r--src/main/java/org/traccar/handler/events/MotionEventHandler.java17
-rw-r--r--src/main/java/org/traccar/helper/model/AttributeUtil.java106
-rw-r--r--src/main/java/org/traccar/model/ExtendedModel.java8
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java26
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocol.java4
-rw-r--r--src/main/java/org/traccar/reports/common/ReportUtils.java7
-rw-r--r--src/main/java/org/traccar/reports/common/TripsConfig.java39
-rw-r--r--src/main/java/org/traccar/web/CharResponseWrapper.java82
-rw-r--r--src/main/java/org/traccar/web/ModernDefaultServlet.java33
-rw-r--r--src/main/java/org/traccar/web/OverrideFilter.java80
-rw-r--r--src/main/java/org/traccar/web/WebModule.java3
-rw-r--r--src/main/java/org/traccar/web/WebServer.java15
15 files changed, 374 insertions, 92 deletions
diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java
index ab75bdc5d..e6556189a 100644
--- a/src/main/java/org/traccar/api/MediaFilter.java
+++ b/src/main/java/org/traccar/api/MediaFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,7 +32,6 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -58,10 +57,6 @@ public class MediaFilter implements Filter {
}
@Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
-
- @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
@@ -99,8 +94,4 @@ public class MediaFilter implements Filter {
}
}
- @Override
- public void destroy() {
- }
-
}
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index c69289403..162a3fe13 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -373,14 +373,15 @@ public final class Keys {
*/
public static final ConfigKey<Boolean> EVENT_MOTION_PROCESS_INVALID_POSITIONS = new BooleanConfigKey(
"event.motion.processInvalidPositions",
- List.of(KeyType.CONFIG));
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
+ false);
/**
* If the speed is above specified value, the object is considered to be in motion. Default value is 0.01 knots.
*/
public static final ConfigKey<Double> EVENT_MOTION_SPEED_THRESHOLD = new DoubleConfigKey(
"event.motion.speedThreshold",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
0.01);
/**
@@ -1167,7 +1168,7 @@ public final class Keys {
*/
public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DISTANCE = new LongConfigKey(
"report.trip.minimalTripDistance",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
500L);
/**
@@ -1175,7 +1176,7 @@ public final class Keys {
*/
public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DURATION = new LongConfigKey(
"report.trip.minimalTripDuration",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
300L);
/**
@@ -1183,7 +1184,7 @@ public final class Keys {
*/
public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_PARKING_DURATION = new LongConfigKey(
"report.trip.minimalParkingDuration",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
300L);
/**
@@ -1191,7 +1192,7 @@ public final class Keys {
*/
public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_NO_DATA_DURATION = new LongConfigKey(
"report.trip.minimalNoDataDuration",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
3600L);
/**
@@ -1199,7 +1200,8 @@ public final class Keys {
*/
public static final ConfigKey<Boolean> REPORT_TRIP_USE_IGNITION = new BooleanConfigKey(
"report.trip.useIgnition",
- List.of(KeyType.CONFIG));
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
+ false);
/**
* Ignore odometer value reported by the device and use server-calculated total distance instead. This is useful
@@ -1207,7 +1209,8 @@ public final class Keys {
*/
public static final ConfigKey<Boolean> REPORT_IGNORE_ODOMETER = new BooleanConfigKey(
"report.ignoreOdometer",
- List.of(KeyType.CONFIG));
+ List.of(KeyType.CONFIG),
+ false);
/**
* Boolean flag to enable or disable position filtering.
diff --git a/src/main/java/org/traccar/handler/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java
index 10312f9b3..297527b4d 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,8 +18,10 @@ package org.traccar.handler;
import io.netty.channel.ChannelHandler;
import org.traccar.BaseDataHandler;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Position;
-import org.traccar.reports.common.TripsConfig;
+import org.traccar.session.cache.CacheManager;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -28,17 +30,19 @@ import javax.inject.Singleton;
@ChannelHandler.Sharable
public class MotionHandler extends BaseDataHandler {
- private final double speedThreshold;
+ private final CacheManager cacheManager;
@Inject
- public MotionHandler(TripsConfig tripsConfig) {
- speedThreshold = tripsConfig.getSpeedThreshold();
+ public MotionHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
}
@Override
protected Position handlePosition(Position position) {
if (!position.hasAttribute(Position.KEY_MOTION)) {
- position.set(Position.KEY_MOTION, position.getSpeed() > speedThreshold);
+ double threshold = AttributeUtil.lookup(
+ cacheManager, Keys.EVENT_MOTION_SPEED_THRESHOLD, position.getDeviceId());
+ position.set(Position.KEY_MOTION, position.getSpeed() > threshold);
}
return position;
}
diff --git a/src/main/java/org/traccar/handler/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
index c406bd935..d2c54ccf3 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,8 @@ package org.traccar.handler.events;
import io.netty.channel.ChannelHandler;
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;
@@ -46,14 +48,11 @@ public class MotionEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
private final Storage storage;
- private final TripsConfig tripsConfig;
@Inject
- public MotionEventHandler(
- CacheManager cacheManager, Storage storage, TripsConfig tripsConfig) {
+ public MotionEventHandler(CacheManager cacheManager, Storage storage) {
this.cacheManager = cacheManager;
this.storage = storage;
- this.tripsConfig = tripsConfig;
}
@Override
@@ -61,14 +60,16 @@ public class MotionEventHandler extends BaseEventHandler {
long deviceId = position.getDeviceId();
Device device = cacheManager.getObject(Device.class, deviceId);
- if (device == null) {
+ if (device == null || !PositionUtil.isLatest(cacheManager, position)) {
return null;
}
- if (!PositionUtil.isLatest(cacheManager, position)
- || !tripsConfig.getProcessInvalidPositions() && !position.getValid()) {
+ boolean processInvalid = AttributeUtil.lookup(
+ cacheManager, Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS, deviceId);
+ if (!processInvalid && !position.getValid()) {
return null;
}
+ TripsConfig tripsConfig = new TripsConfig(new AttributeUtil.CacheProvider(cacheManager, deviceId));
MotionState state = MotionState.fromDevice(device);
MotionProcessor.updateState(state, position, position.getBoolean(Position.KEY_MOTION), tripsConfig);
if (state.isChanged()) {
diff --git a/src/main/java/org/traccar/helper/model/AttributeUtil.java b/src/main/java/org/traccar/helper/model/AttributeUtil.java
index 43558e8f7..2630f64f0 100644
--- a/src/main/java/org/traccar/helper/model/AttributeUtil.java
+++ b/src/main/java/org/traccar/helper/model/AttributeUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,25 +15,44 @@
*/
package org.traccar.helper.model;
+import org.traccar.api.security.PermissionsService;
+import org.traccar.config.Config;
import org.traccar.config.ConfigKey;
import org.traccar.config.KeyType;
import org.traccar.config.Keys;
import org.traccar.model.Device;
import org.traccar.model.Group;
+import org.traccar.model.Server;
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 final class AttributeUtil {
private AttributeUtil() {
}
- @SuppressWarnings({ "deprecation", "unchecked" })
+ public interface Provider {
+ Device getDevice();
+ Group getGroup(long groupId);
+ Server getServer();
+ Config getConfig();
+ }
+
public static <T> T lookup(CacheManager cacheManager, ConfigKey<T> key, long deviceId) {
- Device device = cacheManager.getObject(Device.class, deviceId);
+ return lookup(new CacheProvider(cacheManager, deviceId), key);
+ }
+
+ @SuppressWarnings({ "deprecation", "unchecked" })
+ public static <T> T lookup(Provider provider, ConfigKey<T> key) {
+ Device device = provider.getDevice();
Object result = device.getAttributes().get(key.getKey());
long groupId = device.getGroupId();
while (result == null && groupId > 0) {
- Group group = cacheManager.getObject(Group.class, groupId);
+ Group group = provider.getGroup(groupId);
if (group != null) {
result = group.getAttributes().get(key.getKey());
groupId = group.getGroupId();
@@ -42,10 +61,10 @@ public final class AttributeUtil {
}
}
if (result == null && key.hasType(KeyType.SERVER)) {
- result = cacheManager.getServer().getAttributes().get(key.getKey());
+ result = provider.getServer().getAttributes().get(key.getKey());
}
if (result == null && key.hasType(KeyType.CONFIG)) {
- result = cacheManager.getConfig().getString(key.getKey());
+ result = provider.getConfig().getString(key.getKey());
}
if (result != null) {
@@ -91,4 +110,79 @@ public final class AttributeUtil {
return defaultPassword;
}
+ public static class CacheProvider implements Provider {
+
+ private final CacheManager cacheManager;
+ private final long deviceId;
+
+ public CacheProvider(CacheManager cacheManager, long deviceId) {
+ this.cacheManager = cacheManager;
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public Device getDevice() {
+ return cacheManager.getObject(Device.class, deviceId);
+ }
+
+ @Override
+ public Group getGroup(long groupId) {
+ return cacheManager.getObject(Group.class, groupId);
+ }
+
+ @Override
+ public Server getServer() {
+ return cacheManager.getServer();
+ }
+
+ @Override
+ public Config getConfig() {
+ return cacheManager.getConfig();
+ }
+ }
+
+ public static class StorageProvider implements Provider {
+
+ private final Config config;
+ private final Storage storage;
+ private final PermissionsService permissionsService;
+ private final Device device;
+
+ public StorageProvider(Config config, Storage storage, PermissionsService permissionsService, Device device) {
+ this.config = config;
+ this.storage = storage;
+ this.permissionsService = permissionsService;
+ this.device = device;
+ }
+
+ @Override
+ public Device getDevice() {
+ return device;
+ }
+
+ @Override
+ public Group getGroup(long groupId) {
+ try {
+ return storage.getObject(
+ Group.class, new Request(new Columns.All(), new Condition.Equals("id", groupId)));
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Server getServer() {
+ try {
+ return permissionsService.getServer();
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Config getConfig() {
+ return config;
+ }
+ }
+
}
diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java
index 7a61eda8c..6a4f502f7 100644
--- a/src/main/java/org/traccar/model/ExtendedModel.java
+++ b/src/main/java/org/traccar/model/ExtendedModel.java
@@ -89,14 +89,18 @@ public class ExtendedModel extends BaseModel {
}
}
- public String getString(String key) {
+ public String getString(String key, String defaultValue) {
if (attributes.containsKey(key)) {
return attributes.get(key).toString();
} else {
- return null;
+ return defaultValue;
}
}
+ public String getString(String key) {
+ return getString(key, null);
+ }
+
public double getDouble(String key) {
if (attributes.containsKey(key)) {
Object value = attributes.get(key);
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index 05e2fb8cc..4beee7696 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -333,10 +333,6 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // power level
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
break;
- case 0x60:
- position.set(Position.KEY_EVENT, buf.readUnsignedShort());
- buf.skipBytes(length - 2);
- break;
case 0x61:
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
break;
@@ -488,6 +484,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01);
}
break;
+ case 0x60:
+ position.set(Position.KEY_EVENT, buf.readUnsignedShort());
+ buf.skipBytes(length - 2);
+ break;
+ case 0x69:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ break;
case 0x80:
buf.readUnsignedByte(); // content
endIndex = buf.writerIndex() - 2;
@@ -934,6 +937,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
int id = buf.readUnsignedByte();
int length = buf.readUnsignedByte();
switch (id) {
+ case 0x01:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
+ break;
+ case 0x02:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
case 0x1A:
position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
break;
@@ -951,6 +960,15 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
case 0x23:
position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING);
break;
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ break;
+ case 0x31:
+ case 0x32:
+ position.set(Position.KEY_ALARM, Position.ALARM_DOOR);
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/protocol/StartekProtocol.java b/src/main/java/org/traccar/protocol/StartekProtocol.java
index d010df858..1b1c93e33 100644
--- a/src/main/java/org/traccar/protocol/StartekProtocol.java
+++ b/src/main/java/org/traccar/protocol/StartekProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2023 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ public class StartekProtocol extends BaseProtocol {
addServer(new TrackerServer(config, getName(), false) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
- pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new LineBasedFrameDecoder(1100));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StartekProtocolEncoder(StartekProtocol.this));
diff --git a/src/main/java/org/traccar/reports/common/ReportUtils.java b/src/main/java/org/traccar/reports/common/ReportUtils.java
index 0c168e4ef..f5aa6d040 100644
--- a/src/main/java/org/traccar/reports/common/ReportUtils.java
+++ b/src/main/java/org/traccar/reports/common/ReportUtils.java
@@ -31,6 +31,7 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.geocoder.Geocoder;
import org.traccar.helper.UnitsConverter;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.helper.model.PositionUtil;
import org.traccar.helper.model.UserUtil;
import org.traccar.model.BaseModel;
@@ -67,18 +68,16 @@ public class ReportUtils {
private final Config config;
private final Storage storage;
private final PermissionsService permissionsService;
- private final TripsConfig tripsConfig;
private final VelocityEngine velocityEngine;
private final Geocoder geocoder;
@Inject
public ReportUtils(
Config config, Storage storage, PermissionsService permissionsService,
- TripsConfig tripsConfig, VelocityEngine velocityEngine, @Nullable Geocoder geocoder) {
+ VelocityEngine velocityEngine, @Nullable Geocoder geocoder) {
this.config = config;
this.storage = storage;
this.permissionsService = permissionsService;
- this.tripsConfig = tripsConfig;
this.velocityEngine = velocityEngine;
this.geocoder = geocoder;
}
@@ -306,6 +305,8 @@ public class ReportUtils {
Class<T> reportClass) throws StorageException {
Collection<T> result = new ArrayList<>();
+ TripsConfig tripsConfig = new TripsConfig(
+ new AttributeUtil.StorageProvider(config, storage, permissionsService, device));
ArrayList<Position> positions = new ArrayList<>(positionCollection);
if (!positions.isEmpty()) {
diff --git a/src/main/java/org/traccar/reports/common/TripsConfig.java b/src/main/java/org/traccar/reports/common/TripsConfig.java
index 52db97b74..2792114d4 100644
--- a/src/main/java/org/traccar/reports/common/TripsConfig.java
+++ b/src/main/java/org/traccar/reports/common/TripsConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,37 +16,28 @@
*/
package org.traccar.reports.common;
-import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-@Singleton
public class TripsConfig {
- @Inject
- public TripsConfig(Config config) {
+ public TripsConfig(AttributeUtil.Provider attributeProvider) {
this(
- config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE),
- config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION) * 1000,
- config.getLong(Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION) * 1000,
- config.getLong(Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION) * 1000,
- config.getBoolean(Keys.REPORT_TRIP_USE_IGNITION),
- config.getBoolean(Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS),
- config.getDouble(Keys.EVENT_MOTION_SPEED_THRESHOLD));
+ AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE),
+ AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION) * 1000,
+ AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION) * 1000,
+ AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION) * 1000,
+ AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_USE_IGNITION));
}
public TripsConfig(
double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration,
- long minimalNoDataDuration, boolean useIgnition, boolean processInvalidPositions, double speedThreshold) {
+ long minimalNoDataDuration, boolean useIgnition) {
this.minimalTripDistance = minimalTripDistance;
this.minimalTripDuration = minimalTripDuration;
this.minimalParkingDuration = minimalParkingDuration;
this.minimalNoDataDuration = minimalNoDataDuration;
this.useIgnition = useIgnition;
- this.processInvalidPositions = processInvalidPositions;
- this.speedThreshold = speedThreshold;
}
private final double minimalTripDistance;
@@ -79,16 +70,4 @@ public class TripsConfig {
return useIgnition;
}
- private final boolean processInvalidPositions;
-
- public boolean getProcessInvalidPositions() {
- return processInvalidPositions;
- }
-
- private final double speedThreshold;
-
- public double getSpeedThreshold() {
- return speedThreshold;
- }
-
}
diff --git a/src/main/java/org/traccar/web/CharResponseWrapper.java b/src/main/java/org/traccar/web/CharResponseWrapper.java
new file mode 100644
index 000000000..477fe7928
--- /dev/null
+++ b/src/main/java/org/traccar/web/CharResponseWrapper.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.web;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class CharResponseWrapper extends HttpServletResponseWrapper {
+
+ private final ByteArrayOutputStream capture;
+ private ServletOutputStream output;
+
+ public CharResponseWrapper(HttpServletResponse response) {
+ super(response);
+ capture = new ByteArrayOutputStream(response.getBufferSize());
+ }
+
+ @Override
+ public ServletOutputStream getOutputStream() {
+ if (output == null) {
+ output = new ServletOutputStream() {
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ @Override
+ public void setWriteListener(WriteListener writeListener) {
+ }
+
+ @Override
+ public void write(int b) {
+ capture.write(b);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ capture.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ capture.close();
+ }
+ };
+ }
+ return output;
+ }
+
+ @Override
+ public void flushBuffer() throws IOException {
+ super.flushBuffer();
+ if (output != null) {
+ output.flush();
+ }
+ }
+
+ public byte[] getCapture() throws IOException {
+ if (output != null) {
+ output.close();
+ }
+ return capture.toByteArray();
+ }
+
+}
diff --git a/src/main/java/org/traccar/web/ModernDefaultServlet.java b/src/main/java/org/traccar/web/ModernDefaultServlet.java
new file mode 100644
index 000000000..bae089d6c
--- /dev/null
+++ b/src/main/java/org/traccar/web/ModernDefaultServlet.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.web;
+
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.util.resource.Resource;
+
+public class ModernDefaultServlet extends DefaultServlet {
+
+ @Override
+ public Resource getResource(String pathInContext) {
+ return super.getResource(pathInContext.indexOf('.') < 0 ? "/" : pathInContext);
+ }
+
+ @Override
+ public String getWelcomeFile(String pathInContext) {
+ return super.getWelcomeFile("/");
+ }
+
+}
diff --git a/src/main/java/org/traccar/web/OverrideFilter.java b/src/main/java/org/traccar/web/OverrideFilter.java
new file mode 100644
index 000000000..708632bc1
--- /dev/null
+++ b/src/main/java/org/traccar/web/OverrideFilter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.web;
+
+import com.google.inject.Provider;
+import org.traccar.api.security.PermissionsService;
+import org.traccar.model.Server;
+import org.traccar.storage.StorageException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Singleton
+public class OverrideFilter implements Filter {
+
+ private final Provider<PermissionsService> permissionsServiceProvider;
+
+ @Inject
+ public OverrideFilter(Provider<PermissionsService> permissionsServiceProvider) {
+ this.permissionsServiceProvider = permissionsServiceProvider;
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ CharResponseWrapper wrappedResponse = new CharResponseWrapper((HttpServletResponse) response);
+
+ chain.doFilter(request, wrappedResponse);
+
+ byte[] bytes = wrappedResponse.getCapture();
+ if (wrappedResponse.getContentType().contains("text/html")
+ || ((HttpServletRequest) request).getPathInfo().endsWith("manifest.json")) {
+
+ Server server;
+ try {
+ server = permissionsServiceProvider.get().getServer();
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ }
+
+ String title = server.getString("title", "Traccar");
+ String description = server.getString("description", "Traccar GPS Tracking System");
+ String colorPrimary = server.getString("colorPrimary", "#1a237e");
+
+ String alteredContent = new String(wrappedResponse.getCapture())
+ .replace("${title}", title)
+ .replace("${description}", description)
+ .replace("${colorPrimary}", colorPrimary);
+
+ response.setContentLength(alteredContent.length());
+ response.getOutputStream().write(alteredContent.getBytes());
+
+ } else {
+ response.getOutputStream().write(bytes);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/web/WebModule.java b/src/main/java/org/traccar/web/WebModule.java
index 0722c5d1e..a32a6f447 100644
--- a/src/main/java/org/traccar/web/WebModule.java
+++ b/src/main/java/org/traccar/web/WebModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ public class WebModule extends ServletModule {
@Override
protected void configureServlets() {
+ filter("/*").through(OverrideFilter.class);
filter("/api/*").through(ThrottlingFilter.class);
filter("/api/media/*").through(MediaFilter.class);
serve("/api/socket").with(AsyncSocketServlet.class);
diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java
index 79d19cc9b..ce1220157 100644
--- a/src/main/java/org/traccar/web/WebServer.java
+++ b/src/main/java/org/traccar/web/WebServer.java
@@ -62,9 +62,6 @@ import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.InetSocketAddress;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.EnumSet;
public class WebServer implements LifecycleObject {
@@ -103,14 +100,8 @@ public class WebServer implements LifecycleObject {
@Override
protected void handleErrorPage(
HttpServletRequest request, Writer writer, int code, String message) throws IOException {
- Path index = Paths.get(config.getString(Keys.WEB_PATH), "index.html");
- if (code == HttpStatus.NOT_FOUND_404
- && !request.getPathInfo().startsWith("/api/") && Files.exists(index)) {
- writer.write(Files.readString(index));
- } else {
- writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>"
- + code + " - " + HttpStatus.getMessage(code) + "</body></html>");
- }
+ writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>"
+ + code + " - " + HttpStatus.getMessage(code) + "</body></html>");
}
});
@@ -150,7 +141,7 @@ public class WebServer implements LifecycleObject {
}
private void initWebApp(ServletContextHandler servletHandler) {
- ServletHolder servletHolder = new ServletHolder(DefaultServlet.class);
+ ServletHolder servletHolder = new ServletHolder(ModernDefaultServlet.class);
servletHolder.setInitParameter("resourceBase", new File(config.getString(Keys.WEB_PATH)).getAbsolutePath());
servletHolder.setInitParameter("dirAllowed", "false");
if (config.getBoolean(Keys.WEB_DEBUG)) {