From 00b91d01d89a32710baa9e580bdf581dae7aa711 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 30 May 2022 16:44:20 -0700 Subject: Combine session related classes --- .../java/org/traccar/session/DeviceSession.java | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/main/java/org/traccar/session/DeviceSession.java (limited to 'src/main/java/org/traccar/session/DeviceSession.java') diff --git a/src/main/java/org/traccar/session/DeviceSession.java b/src/main/java/org/traccar/session/DeviceSession.java new file mode 100644 index 000000000..6fe5b5d57 --- /dev/null +++ b/src/main/java/org/traccar/session/DeviceSession.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 - 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.session; + +import java.util.TimeZone; + +public class DeviceSession { + + private final long deviceId; + + public DeviceSession(long deviceId) { + this.deviceId = deviceId; + } + + public long getDeviceId() { + return deviceId; + } + + private TimeZone timeZone; + + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + } + + public TimeZone getTimeZone() { + return timeZone; + } + +} -- cgit v1.2.3 From 9a68d1045f30bf8397d6cbf90df8f42f40979591 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 30 May 2022 16:58:34 -0700 Subject: Add generic variable store --- .../org/traccar/protocol/Gt06ProtocolDecoder.java | 28 +++++++++++----------- .../traccar/protocol/HuabaoProtocolDecoder.java | 8 +++---- .../traccar/protocol/UlbotechProtocolDecoder.java | 8 ++++--- .../java/org/traccar/session/DeviceSession.java | 24 ++++++++++++++----- 4 files changed, 41 insertions(+), 27 deletions(-) (limited to 'src/main/java/org/traccar/session/DeviceSession.java') diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index c200c6ba9..0f89597ce 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -444,8 +444,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return null; } position.setDeviceId(deviceSession.getDeviceId()); - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } } @@ -455,8 +455,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // type deviceSession = getDeviceSession(channel, remoteAddress, imei); - if (deviceSession != null && deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + if (deviceSession != null && !deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } if (dataLength > 10) { @@ -468,7 +468,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { offset = -offset; } if (deviceSession != null) { - TimeZone timeZone = deviceSession.getTimeZone(); + TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE); if (timeZone.getRawOffset() == 0) { timeZone.setRawOffset(offset * 1000); deviceSession.setTimeZone(timeZone); @@ -531,7 +531,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedInt(); // data and alarm - decodeGps(position, buf, false, deviceSession.getTimeZone()); + decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE)); buf.readUnsignedShort(); // terminal info @@ -653,7 +653,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5; - DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone()) + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); @@ -769,7 +769,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } if (hasGps(type)) { - decodeGps(position, buf, false, deviceSession.getTimeZone()); + decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE)); } else { getLastLocation(position, null); } @@ -858,9 +858,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { boolean extendedAlarm = dataLength > 7; if (extendedAlarm) { - decodeGps(position, buf, false, false, false, deviceSession.getTimeZone()); + decodeGps(position, buf, false, false, false, deviceSession.get(DeviceSession.KEY_TIMEZONE)); } else { - DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone()) + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); getLastLocation(position, dateBuilder.getDate()); @@ -925,8 +925,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return null; } - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } Position position = new Position(getProtocolName()); @@ -1033,7 +1033,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_AZ735_GPS || type == MSG_AZ735_ALARM) { - if (!decodeGps(position, buf, true, deviceSession.getTimeZone())) { + if (!decodeGps(position, buf, true, deviceSession.get(DeviceSession.KEY_TIMEZONE))) { getLastLocation(position, position.getDeviceTime()); } @@ -1078,7 +1078,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_OBD) { - DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone()) + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index c75fd673a..00093c978 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -198,8 +198,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { return null; } - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId(), "GMT+8")); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId(), "GMT+8")); } if (type == MSG_TERMINAL_REGISTER) { @@ -407,7 +407,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(buf.readShort()); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1)); position.setCourse(buf.readUnsignedShort()); - position.setTime(readDate(buf, deviceSession.getTimeZone())); + position.setTime(readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE))); if (buf.readableBytes() == 20) { @@ -642,7 +642,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - Date time = readDate(buf, deviceSession.getTimeZone()); + Date time = readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE)); if (buf.readUnsignedByte() > 0) { position.set(Position.KEY_ARCHIVE, true); diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java index e6cc0a891..c9b35158e 100644 --- a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java @@ -37,6 +37,7 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.TimeZone; import java.util.regex.Pattern; public class UlbotechProtocolDecoder extends BaseProtocolDecoder { @@ -214,16 +215,17 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder { return null; } - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); + TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE); long seconds = buf.readUnsignedInt() & 0x7fffffffL; seconds += 946684800L; // 2000-01-01 00:00 - seconds -= deviceSession.getTimeZone().getRawOffset() / 1000; + seconds -= timeZone.getRawOffset() / 1000; Date time = new Date(seconds * 1000); boolean hasLocation = false; diff --git a/src/main/java/org/traccar/session/DeviceSession.java b/src/main/java/org/traccar/session/DeviceSession.java index 6fe5b5d57..0d5b283fe 100644 --- a/src/main/java/org/traccar/session/DeviceSession.java +++ b/src/main/java/org/traccar/session/DeviceSession.java @@ -15,7 +15,8 @@ */ package org.traccar.session; -import java.util.TimeZone; +import java.util.HashMap; +import java.util.Map; public class DeviceSession { @@ -29,14 +30,25 @@ public class DeviceSession { return deviceId; } - private TimeZone timeZone; + public static final String KEY_TIMEZONE = "timezone"; - public void setTimeZone(TimeZone timeZone) { - this.timeZone = timeZone; + private final Map locals = new HashMap<>(); + + public boolean contains(String key) { + return locals.containsKey(key); + } + + public void set(String key, Object value) { + if (value != null) { + locals.put(key, value); + } else { + locals.remove(key); + } } - public TimeZone getTimeZone() { - return timeZone; + @SuppressWarnings("unchecked") + public T get(String key) { + return (T) locals.get(key); } } -- cgit v1.2.3 From 4dc602d8a7700924b0117424533046b28f4a8df4 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Mon, 30 May 2022 18:39:50 -0700 Subject: Combine active device and session --- src/main/java/org/traccar/BaseProtocolDecoder.java | 99 +--------------------- src/main/java/org/traccar/MainEventHandler.java | 2 +- src/main/java/org/traccar/config/Keys.java | 14 --- .../java/org/traccar/database/CommandsManager.java | 12 +-- .../java/org/traccar/database/DeviceManager.java | 6 +- .../java/org/traccar/database/IdentityManager.java | 2 +- .../org/traccar/protocol/EgtsProtocolDecoder.java | 2 +- .../org/traccar/protocol/Gt06ProtocolDecoder.java | 2 +- .../traccar/protocol/OrbcommProtocolDecoder.java | 3 +- .../java/org/traccar/session/ActiveDevice.java | 58 ------------- .../org/traccar/session/ConnectionManager.java | 81 +++++++++++++++--- .../java/org/traccar/session/DeviceSession.java | 40 ++++++++- src/main/java/org/traccar/session/Endpoint.java | 58 +++++++++++++ src/test/java/org/traccar/BaseTest.java | 27 +++++- 14 files changed, 208 insertions(+), 198 deletions(-) delete mode 100644 src/main/java/org/traccar/session/ActiveDevice.java create mode 100644 src/main/java/org/traccar/session/Endpoint.java (limited to 'src/main/java/org/traccar/session/DeviceSession.java') diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java index 3fc6e7697..d6c571b79 100644 --- a/src/main/java/org/traccar/BaseProtocolDecoder.java +++ b/src/main/java/org/traccar/BaseProtocolDecoder.java @@ -18,14 +18,9 @@ package org.traccar; import com.google.inject.Inject; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; -import io.netty.channel.socket.DatagramChannel; -import io.netty.handler.codec.http.HttpRequestDecoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.CommandsManager; -import org.traccar.session.ConnectionManager; import org.traccar.database.IdentityManager; import org.traccar.database.MediaManager; import org.traccar.database.StatisticsManager; @@ -33,22 +28,19 @@ import org.traccar.helper.UnitsConverter; import org.traccar.model.Command; import org.traccar.model.Device; import org.traccar.model.Position; +import org.traccar.session.ConnectionManager; import org.traccar.session.DeviceSession; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collection; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.TimeZone; public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { - private static final Logger LOGGER = LoggerFactory.getLogger(BaseProtocolDecoder.class); - private static final String PROTOCOL_UNKNOWN = "unknown"; private final Protocol protocol; @@ -147,95 +139,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { return result; } - private DeviceSession channelDeviceSession; // connection-based protocols - private final Map addressDeviceSessions = new HashMap<>(); // connectionless protocols - - private long findDeviceId(SocketAddress remoteAddress, String... uniqueIds) { - if (uniqueIds.length > 0) { - long deviceId = 0; - Device device = null; - try { - for (String uniqueId : uniqueIds) { - if (uniqueId != null) { - device = identityManager.getByUniqueId(uniqueId); - if (device != null) { - deviceId = device.getId(); - break; - } - } - } - } catch (Exception e) { - LOGGER.warn("Find device error", e); - } - if (deviceId == 0 && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) { - return identityManager.addUnknownDevice(uniqueIds[0]); - } - if (device != null && !device.getDisabled()) { - return deviceId; - } - StringBuilder message = new StringBuilder(); - if (deviceId == 0) { - message.append("Unknown device -"); - } else { - message.append("Disabled device -"); - } - for (String uniqueId : uniqueIds) { - message.append(" ").append(uniqueId); - } - if (remoteAddress != null) { - message.append(" (").append(((InetSocketAddress) remoteAddress).getHostString()).append(")"); - } - LOGGER.warn(message.toString()); - } - return 0; - } - public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) { - return getDeviceSession(channel, remoteAddress, false, uniqueIds); - } - - public DeviceSession getDeviceSession( - Channel channel, SocketAddress remoteAddress, boolean ignoreCache, String... uniqueIds) { - if (channel != null && BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) != null - || ignoreCache || config.getBoolean(Keys.PROTOCOL_IGNORE_SESSIONS_CACHE.withPrefix(getProtocolName())) - || config.getBoolean(Keys.DECODER_IGNORE_SESSIONS_CACHE)) { - long deviceId = findDeviceId(remoteAddress, uniqueIds); - if (deviceId != 0) { - if (connectionManager != null) { - connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress); - } - return new DeviceSession(deviceId); - } else { - return null; - } - } - if (channel instanceof DatagramChannel) { - long deviceId = findDeviceId(remoteAddress, uniqueIds); - DeviceSession deviceSession = addressDeviceSessions.get(remoteAddress); - if (deviceSession != null && (deviceSession.getDeviceId() == deviceId || uniqueIds.length == 0)) { - return deviceSession; - } else if (deviceId != 0) { - deviceSession = new DeviceSession(deviceId); - addressDeviceSessions.put(remoteAddress, deviceSession); - if (connectionManager != null) { - connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress); - } - return deviceSession; - } else { - return null; - } - } else { - if (channelDeviceSession == null) { - long deviceId = findDeviceId(remoteAddress, uniqueIds); - if (deviceId != 0) { - channelDeviceSession = new DeviceSession(deviceId); - if (connectionManager != null) { - connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress); - } - } - } - return channelDeviceSession; - } + return connectionManager.getDeviceSession(protocol, channel, remoteAddress, uniqueIds); } public void getLastLocation(Position position, Date deviceTime) { diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java index 91706222a..0a25b7547 100644 --- a/src/main/java/org/traccar/MainEventHandler.java +++ b/src/main/java/org/traccar/MainEventHandler.java @@ -130,7 +130,7 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { if (BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName())) { - Context.getConnectionManager().removeActiveDevice(ctx.channel()); + Context.getConnectionManager().removeDeviceSessions(ctx.channel()); } } diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index eebdf7172..f5370874d 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -190,13 +190,6 @@ public final class Keys { ".server", Collections.singletonList(KeyType.GLOBAL)); - /** - * Skip device connection session cache. Per protocol configuration. - */ - public static final ConfigSuffix PROTOCOL_IGNORE_SESSIONS_CACHE = new ConfigSuffix<>( - ".ignoreSessionCache", - Collections.singletonList(KeyType.GLOBAL)); - /** * ORBCOMM API access id. */ @@ -211,13 +204,6 @@ public final class Keys { "orbcomm.password", Collections.singletonList(KeyType.GLOBAL)); - /** - * Skip device connection session cache. Global configuration. - */ - public static final ConfigKey DECODER_IGNORE_SESSIONS_CACHE = new ConfigKey<>( - "decoder.ignoreSessionCache", - Collections.singletonList(KeyType.GLOBAL)); - /** * Server wide connection timeout value in seconds. See protocol timeout for more information. */ diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java index 3adf5d2e9..57ce0f9a4 100644 --- a/src/main/java/org/traccar/database/CommandsManager.java +++ b/src/main/java/org/traccar/database/CommandsManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,7 +34,7 @@ import org.traccar.Context; import org.traccar.model.Command; import org.traccar.model.Typed; import org.traccar.model.Position; -import org.traccar.session.ActiveDevice; +import org.traccar.session.DeviceSession; public class CommandsManager extends ExtendedObjectManager { @@ -75,10 +75,10 @@ public class CommandsManager extends ExtendedObjectManager { throw new RuntimeException("Command " + command.getType() + " is not supported"); } } else { - ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId); - if (activeDevice != null) { - if (activeDevice.supportsLiveCommands()) { - activeDevice.sendCommand(command); + DeviceSession deviceSession = Context.getConnectionManager().getDeviceSession(deviceId); + if (deviceSession != null) { + if (deviceSession.supportsLiveCommands()) { + deviceSession.sendCommand(command); } else { getDeviceQueue(deviceId).add(command); return false; diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java index 0e5056e57..b1ea0b8b7 100644 --- a/src/main/java/org/traccar/database/DeviceManager.java +++ b/src/main/java/org/traccar/database/DeviceManager.java @@ -68,7 +68,7 @@ public class DeviceManager extends BaseObjectManager implements Identity } @Override - public long addUnknownDevice(String uniqueId) { + public Device addUnknownDevice(String uniqueId) { Device device = new Device(); device.setName(uniqueId); device.setUniqueId(uniqueId); @@ -89,10 +89,10 @@ public class DeviceManager extends BaseObjectManager implements Identity Context.getPermissionsManager().refreshAllExtendedPermissions(); } - return device.getId(); + return device; } catch (StorageException e) { LOGGER.warn("Automatic device registration error", e); - return 0; + return null; } } diff --git a/src/main/java/org/traccar/database/IdentityManager.java b/src/main/java/org/traccar/database/IdentityManager.java index af6a6ce71..ee386fdfd 100644 --- a/src/main/java/org/traccar/database/IdentityManager.java +++ b/src/main/java/org/traccar/database/IdentityManager.java @@ -20,7 +20,7 @@ import org.traccar.model.Position; public interface IdentityManager { - long addUnknownDevice(String uniqueId); + Device addUnknownDevice(String uniqueId); Device getById(long id); diff --git a/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java b/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java index 3a6af60a1..01d329580 100644 --- a/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java @@ -291,7 +291,7 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { if (serviceType == SERVICE_TELEDATA && position.getValid()) { if (useObjectIdAsDeviceId && objectId != 0L) { - deviceSession = getDeviceSession(channel, remoteAddress, true, String.valueOf(objectId)); + deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(objectId)); if (deviceSession != null) { position.setDeviceId(deviceSession.getDeviceId()); } diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 0f89597ce..4b9757874 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -471,7 +471,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE); if (timeZone.getRawOffset() == 0) { timeZone.setRawOffset(offset * 1000); - deviceSession.setTimeZone(timeZone); + deviceSession.set(DeviceSession.KEY_TIMEZONE, timeZone); } } } diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java index 8ec47908f..1164d72a1 100644 --- a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java @@ -70,8 +70,7 @@ public class OrbcommProtocolDecoder extends BaseProtocolDecoder { JsonArray messages = json.getJsonArray("Messages"); for (int i = 0; i < messages.size(); i++) { JsonObject message = messages.getJsonObject(i); - DeviceSession deviceSession = getDeviceSession( - channel, remoteAddress, true, message.getString("MobileID")); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, message.getString("MobileID")); if (deviceSession != null) { Position position = new Position(getProtocolName()); diff --git a/src/main/java/org/traccar/session/ActiveDevice.java b/src/main/java/org/traccar/session/ActiveDevice.java deleted file mode 100644 index af19ba55b..000000000 --- a/src/main/java/org/traccar/session/ActiveDevice.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 - 2020 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.session; - -import io.netty.channel.Channel; -import io.netty.handler.codec.http.HttpRequestDecoder; -import org.traccar.BasePipelineFactory; -import org.traccar.Protocol; -import org.traccar.model.Command; - -import java.net.SocketAddress; - -public class ActiveDevice { - - private final long deviceId; - private final Protocol protocol; - private final Channel channel; - private final SocketAddress remoteAddress; - private final boolean supportsLiveCommands; - - public ActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { - this.deviceId = deviceId; - this.protocol = protocol; - this.channel = channel; - this.remoteAddress = remoteAddress; - supportsLiveCommands = BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) == null; - } - - public Channel getChannel() { - return channel; - } - - public long getDeviceId() { - return deviceId; - } - - public boolean supportsLiveCommands() { - return supportsLiveCommands; - } - - public void sendCommand(Command command) { - protocol.sendDataCommand(channel, remoteAddress, command); - } - -} diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java index fbc15b00d..5d8a8c606 100644 --- a/src/main/java/org/traccar/session/ConnectionManager.java +++ b/src/main/java/org/traccar/session/ConnectionManager.java @@ -31,6 +31,7 @@ import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.storage.StorageException; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Date; import java.util.HashMap; @@ -47,7 +48,9 @@ public class ConnectionManager { private final long deviceTimeout; private final boolean updateDeviceState; - private final Map activeDevices = new ConcurrentHashMap<>(); + private final Map sessionsByDeviceId = new ConcurrentHashMap<>(); + private final Map> sessionsByEndpoint = new ConcurrentHashMap<>(); + private final Map> listeners = new ConcurrentHashMap<>(); private final Map timeouts = new ConcurrentHashMap<>(); @@ -59,22 +62,78 @@ public class ConnectionManager { timer = Main.getInjector().getInstance(Timer.class); } - public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { - activeDevices.put(deviceId, new ActiveDevice(deviceId, protocol, channel, remoteAddress)); + public DeviceSession getDeviceSession(long deviceId) { + return sessionsByDeviceId.get(deviceId); } - public void removeActiveDevice(Channel channel) { - for (ActiveDevice activeDevice : activeDevices.values()) { - if (activeDevice.getChannel() == channel) { - updateDevice(activeDevice.getDeviceId(), Device.STATUS_OFFLINE, null); - activeDevices.remove(activeDevice.getDeviceId()); - break; + public DeviceSession getDeviceSession( + Protocol protocol, Channel channel, SocketAddress remoteAddress, String... uniqueIds) { + + Endpoint endpoint = new Endpoint(channel, remoteAddress); + Map endpointSessions = sessionsByEndpoint.getOrDefault( + endpoint, new ConcurrentHashMap<>()); + if (uniqueIds.length > 0) { + for (String uniqueId : uniqueIds) { + DeviceSession deviceSession = endpointSessions.get(uniqueId); + if (deviceSession != null) { + return deviceSession; + } } + } else { + return endpointSessions.values().stream().findAny().orElse(null); + } + + Device device = null; + try { + for (String uniqueId : uniqueIds) { + device = Context.getIdentityManager().getByUniqueId(uniqueId); + if (device != null) { + break; + } + } + } catch (Exception e) { + LOGGER.warn("Find device error", e); + } + + if (device == null && Context.getConfig().getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) { + device = Context.getIdentityManager().addUnknownDevice(uniqueIds[0]); + } + + if (device != null && !device.getDisabled()) { + DeviceSession oldSession = sessionsByDeviceId.remove(device.getId()); + if (oldSession != null) { + Endpoint oldEndpoint = new Endpoint(oldSession.getChannel(), oldSession.getRemoteAddress()); + Map oldEndpointSessions = sessionsByEndpoint.get(oldEndpoint); + if (oldEndpointSessions.size() > 1) { + oldEndpointSessions.remove(device.getUniqueId()); + } else { + sessionsByEndpoint.remove(oldEndpoint); + } + } + + DeviceSession deviceSession = new DeviceSession( + device.getId(), device.getUniqueId(), protocol, channel, remoteAddress); + endpointSessions.put(device.getUniqueId(), deviceSession); + sessionsByEndpoint.put(endpoint, endpointSessions); + sessionsByDeviceId.put(device.getId(), deviceSession); + + return deviceSession; + } else { + LOGGER.warn((device == null ? "Unknown" : "Disabled") + " device - " + String.join(" ", uniqueIds) + + " (" + ((InetSocketAddress) remoteAddress).getHostString() + ")"); + return null; } } - public ActiveDevice getActiveDevice(long deviceId) { - return activeDevices.get(deviceId); + public void removeDeviceSessions(Channel channel) { + Endpoint endpoint = new Endpoint(channel, channel.remoteAddress()); + Map endpointSessions = sessionsByEndpoint.remove(endpoint); + if (endpointSessions != null) { + for (DeviceSession deviceSession : endpointSessions.values()) { + updateDevice(deviceSession.getDeviceId(), Device.STATUS_OFFLINE, null); + sessionsByDeviceId.remove(deviceSession.getDeviceId()); + } + } } public void updateDevice(final long deviceId, String status, Date time) { diff --git a/src/main/java/org/traccar/session/DeviceSession.java b/src/main/java/org/traccar/session/DeviceSession.java index 0d5b283fe..009f90f5a 100644 --- a/src/main/java/org/traccar/session/DeviceSession.java +++ b/src/main/java/org/traccar/session/DeviceSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -15,21 +15,57 @@ */ package org.traccar.session; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.HttpRequestDecoder; +import org.traccar.BasePipelineFactory; +import org.traccar.Protocol; +import org.traccar.model.Command; + +import java.net.SocketAddress; import java.util.HashMap; import java.util.Map; public class DeviceSession { private final long deviceId; + private final String uniqueId; + private final Protocol protocol; + private final Channel channel; + private final SocketAddress remoteAddress; - public DeviceSession(long deviceId) { + public DeviceSession( + long deviceId, String uniqueId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { this.deviceId = deviceId; + this.uniqueId = uniqueId; + this.protocol = protocol; + this.channel = channel; + this.remoteAddress = remoteAddress; } public long getDeviceId() { return deviceId; } + public String getUniqueId() { + return uniqueId; + } + + public Channel getChannel() { + return channel; + } + + public SocketAddress getRemoteAddress() { + return remoteAddress; + } + + public boolean supportsLiveCommands() { + return BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) == null; + } + + public void sendCommand(Command command) { + protocol.sendDataCommand(channel, remoteAddress, command); + } + public static final String KEY_TIMEZONE = "timezone"; private final Map locals = new HashMap<>(); diff --git a/src/main/java/org/traccar/session/Endpoint.java b/src/main/java/org/traccar/session/Endpoint.java new file mode 100644 index 000000000..76aac3444 --- /dev/null +++ b/src/main/java/org/traccar/session/Endpoint.java @@ -0,0 +1,58 @@ +/* + * Copyright 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.session; + +import io.netty.channel.Channel; + +import java.net.SocketAddress; +import java.util.Objects; + +public class Endpoint { + + private final Channel channel; + private final SocketAddress remoteAddress; + + public Endpoint(Channel channel, SocketAddress remoteAddress) { + this.channel = channel; + this.remoteAddress = remoteAddress; + } + + public Channel getChannel() { + return channel; + } + + public SocketAddress getRemoteAddress() { + return remoteAddress; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Endpoint endpoint = (Endpoint) o; + return channel.equals(endpoint.channel) && remoteAddress.equals(endpoint.remoteAddress); + } + + @Override + public int hashCode() { + return Objects.hash(channel, remoteAddress); + } + +} diff --git a/src/test/java/org/traccar/BaseTest.java b/src/test/java/org/traccar/BaseTest.java index a33bb2b5d..40ac76601 100644 --- a/src/test/java/org/traccar/BaseTest.java +++ b/src/test/java/org/traccar/BaseTest.java @@ -1,11 +1,22 @@ package org.traccar; +import io.netty.channel.Channel; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.traccar.config.Config; import org.traccar.session.ConnectionManager; import org.traccar.database.IdentityManager; import org.traccar.database.MediaManager; import org.traccar.database.StatisticsManager; import org.traccar.model.Device; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.TimeZone; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -30,7 +41,21 @@ public class BaseTest { when(identityManager.lookupAttributeInteger(anyLong(), any(), anyInt(), anyBoolean(), anyBoolean())) .thenAnswer(invocation -> invocation.getArguments()[2]); decoder.setIdentityManager(identityManager); - decoder.setConnectionManager(mock(ConnectionManager.class)); + var connectionManager = mock(ConnectionManager.class); + var uniqueIdsProvided = new HashSet(); + when(connectionManager.getDeviceSession(any(), any(), any(), any())).thenAnswer(invocation -> { + var mock = new DeviceSession(1L, "", mock(Protocol.class), mock(Channel.class), mock(SocketAddress.class)); + if (uniqueIdsProvided.isEmpty()) { + if (invocation.getArguments().length > 3) { + uniqueIdsProvided.add(true); + return mock; + } + return null; + } else { + return mock; + } + }); + decoder.setConnectionManager(connectionManager); decoder.setStatisticsManager(mock(StatisticsManager.class)); decoder.setMediaManager(mock(MediaManager.class)); return decoder; -- cgit v1.2.3