aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar/database')
-rw-r--r--src/main/java/org/traccar/database/ActiveDevice.java58
-rw-r--r--src/main/java/org/traccar/database/AttributesManager.java36
-rw-r--r--src/main/java/org/traccar/database/BaseObjectManager.java178
-rw-r--r--src/main/java/org/traccar/database/CalendarManager.java27
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java221
-rw-r--r--src/main/java/org/traccar/database/ConnectionManager.java216
-rw-r--r--src/main/java/org/traccar/database/DataManager.java276
-rw-r--r--src/main/java/org/traccar/database/DeviceLookupService.java141
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java471
-rw-r--r--src/main/java/org/traccar/database/DriversManager.java100
-rw-r--r--src/main/java/org/traccar/database/ExtendedObjectManager.java143
-rw-r--r--src/main/java/org/traccar/database/GeofenceManager.java66
-rw-r--r--src/main/java/org/traccar/database/GroupsManager.java83
-rw-r--r--src/main/java/org/traccar/database/IdentityManager.java50
-rw-r--r--src/main/java/org/traccar/database/LdapProvider.java2
-rw-r--r--src/main/java/org/traccar/database/MailManager.java151
-rw-r--r--src/main/java/org/traccar/database/MaintenancesManager.java27
-rw-r--r--src/main/java/org/traccar/database/ManagableObjects.java27
-rw-r--r--src/main/java/org/traccar/database/MediaManager.java19
-rw-r--r--src/main/java/org/traccar/database/NotificationManager.java195
-rw-r--r--src/main/java/org/traccar/database/OpenIdProvider.java203
-rw-r--r--src/main/java/org/traccar/database/OrderManager.java26
-rw-r--r--src/main/java/org/traccar/database/PermissionsManager.java528
-rw-r--r--src/main/java/org/traccar/database/SimpleObjectManager.java100
-rw-r--r--src/main/java/org/traccar/database/StatisticsManager.java44
-rw-r--r--src/main/java/org/traccar/database/UsersManager.java93
26 files changed, 575 insertions, 2906 deletions
diff --git a/src/main/java/org/traccar/database/ActiveDevice.java b/src/main/java/org/traccar/database/ActiveDevice.java
deleted file mode 100644
index c05d56ad2..000000000
--- a/src/main/java/org/traccar/database/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.database;
-
-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/database/AttributesManager.java b/src/main/java/org/traccar/database/AttributesManager.java
deleted file mode 100644
index 28816645a..000000000
--- a/src/main/java/org/traccar/database/AttributesManager.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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.Attribute;
-
-public class AttributesManager extends ExtendedObjectManager<Attribute> {
-
- public AttributesManager(DataManager dataManager) {
- super(dataManager, Attribute.class);
- }
-
- @Override
- public void updateCachedItem(Attribute attribute) {
- Attribute cachedAttribute = getById(attribute.getId());
- cachedAttribute.setDescription(attribute.getDescription());
- cachedAttribute.setAttribute(attribute.getAttribute());
- cachedAttribute.setExpression(attribute.getExpression());
- cachedAttribute.setType(attribute.getType());
- }
-
-}
diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java
deleted file mode 100644
index dd8b3bae4..000000000
--- a/src/main/java/org/traccar/database/BaseObjectManager.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.model.BaseModel;
-import org.traccar.storage.StorageException;
-
-public class BaseObjectManager<T extends BaseModel> {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(BaseObjectManager.class);
-
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- private final DataManager dataManager;
-
- private final Class<T> baseClass;
- private Map<Long, T> items;
-
- protected BaseObjectManager(DataManager dataManager, Class<T> baseClass) {
- this.dataManager = dataManager;
- this.baseClass = baseClass;
- refreshItems();
- }
-
- protected final void readLock() {
- lock.readLock().lock();
- }
-
- protected final void readUnlock() {
- lock.readLock().unlock();
- }
-
- protected final void writeLock() {
- lock.writeLock().lock();
- }
-
- protected final void writeUnlock() {
- lock.writeLock().unlock();
- }
-
- protected final DataManager getDataManager() {
- return dataManager;
- }
-
- protected final Class<T> getBaseClass() {
- return baseClass;
- }
-
- public T getById(long itemId) {
- try {
- readLock();
- return items.get(itemId);
- } finally {
- readUnlock();
- }
- }
-
- public void refreshItems() {
- if (dataManager != null) {
- try {
- writeLock();
- Collection<T> databaseItems = dataManager.getObjects(baseClass);
- if (items == null) {
- items = new ConcurrentHashMap<>(databaseItems.size());
- }
- Set<Long> databaseItemIds = new HashSet<>();
- for (T item : databaseItems) {
- databaseItemIds.add(item.getId());
- if (items.containsKey(item.getId())) {
- updateCachedItem(item);
- } else {
- addNewItem(item);
- }
- }
- for (Long cachedItemId : items.keySet()) {
- if (!databaseItemIds.contains(cachedItemId)) {
- removeCachedItem(cachedItemId);
- }
- }
- } catch (StorageException error) {
- LOGGER.warn("Error refreshing items", error);
- } finally {
- writeUnlock();
- }
- }
- }
-
- protected void addNewItem(T item) {
- try {
- writeLock();
- items.put(item.getId(), item);
- } finally {
- writeUnlock();
- }
- }
-
- public void addItem(T item) throws StorageException {
- dataManager.addObject(item);
- addNewItem(item);
- }
-
- protected void updateCachedItem(T item) {
- try {
- writeLock();
- items.put(item.getId(), item);
- } finally {
- writeUnlock();
- }
- }
-
- public void updateItem(T item) throws StorageException {
- dataManager.updateObject(item);
- updateCachedItem(item);
- }
-
- protected void removeCachedItem(long itemId) {
- try {
- writeLock();
- items.remove(itemId);
- } finally {
- writeUnlock();
- }
- }
-
- public void removeItem(long itemId) throws StorageException {
- BaseModel item = getById(itemId);
- if (item != null) {
- dataManager.removeObject(baseClass, itemId);
- removeCachedItem(itemId);
- }
- }
-
- public final Collection<T> getItems(Set<Long> itemIds) {
- Collection<T> result = new LinkedList<>();
- for (long itemId : itemIds) {
- T item = getById(itemId);
- if (item != null) {
- result.add(item);
- }
- }
- return result;
- }
-
- public Set<Long> getAllItems() {
- try {
- readLock();
- return items.keySet();
- } finally {
- readUnlock();
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/database/CalendarManager.java b/src/main/java/org/traccar/database/CalendarManager.java
deleted file mode 100644
index 44ced1082..000000000
--- a/src/main/java/org/traccar/database/CalendarManager.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@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.Calendar;
-
-public class CalendarManager extends SimpleObjectManager<Calendar> {
-
- public CalendarManager(DataManager dataManager) {
- super(dataManager, Calendar.class);
- }
-
-}
diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index 843c89e82..fb8f2f9d6 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");
@@ -16,177 +16,112 @@
*/
package org.traccar.database;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.traccar.BaseProtocol;
-import org.traccar.Context;
+import org.traccar.ServerManager;
+import org.traccar.broadcast.BroadcastInterface;
+import org.traccar.broadcast.BroadcastService;
import org.traccar.model.Command;
-import org.traccar.model.Typed;
+import org.traccar.model.Device;
import org.traccar.model.Position;
-
-public class CommandsManager extends ExtendedObjectManager<Command> {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(CommandsManager.class);
-
- private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>();
-
- private final boolean queueing;
-
- public CommandsManager(DataManager dataManager, boolean queueing) {
- super(dataManager, Command.class);
- this.queueing = queueing;
- }
-
- public boolean checkDeviceCommand(long deviceId, long commandId) {
- return !getAllDeviceItems(deviceId).contains(commandId);
+import org.traccar.model.QueuedCommand;
+import org.traccar.session.ConnectionManager;
+import org.traccar.session.DeviceSession;
+import org.traccar.sms.SmsManager;
+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.Order;
+import org.traccar.storage.query.Request;
+
+import jakarta.annotation.Nullable;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+@Singleton
+public class CommandsManager implements BroadcastInterface {
+
+ private final Storage storage;
+ private final ServerManager serverManager;
+ private final SmsManager smsManager;
+ private final ConnectionManager connectionManager;
+ private final BroadcastService broadcastService;
+
+ @Inject
+ public CommandsManager(
+ Storage storage, ServerManager serverManager, @Nullable SmsManager smsManager,
+ ConnectionManager connectionManager, BroadcastService broadcastService) {
+ this.storage = storage;
+ this.serverManager = serverManager;
+ this.smsManager = smsManager;
+ this.connectionManager = connectionManager;
+ this.broadcastService = broadcastService;
+ broadcastService.registerListener(this);
}
public boolean sendCommand(Command command) throws Exception {
long deviceId = command.getDeviceId();
- if (command.getId() != 0) {
- command = getById(command.getId()).clone();
- command.setDeviceId(deviceId);
- }
if (command.getTextChannel()) {
- Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
- String phone = Context.getIdentityManager().getById(deviceId).getPhone();
- if (lastPosition != null) {
- BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
- protocol.sendTextCommand(phone, command);
+ if (smsManager == null) {
+ throw new RuntimeException("SMS not configured");
+ }
+ Device device = storage.getObject(Device.class, new Request(
+ new Columns.Include("positionId", "phone"), new Condition.Equals("id", deviceId)));
+ Position position = storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getPositionId())));
+ if (position != null) {
+ BaseProtocol protocol = serverManager.getProtocol(position.getProtocol());
+ protocol.sendTextCommand(device.getPhone(), command);
} else if (command.getType().equals(Command.TYPE_CUSTOM)) {
- if (Context.getSmsManager() != null) {
- Context.getSmsManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true);
- } else {
- throw new RuntimeException("SMS is not enabled");
- }
+ smsManager.sendMessage(device.getPhone(), command.getString(Command.KEY_DATA), true);
} else {
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);
- } else {
- getDeviceQueue(deviceId).add(command);
- return false;
- }
- } else if (!queueing) {
- throw new RuntimeException("Device is not online");
+ DeviceSession deviceSession = connectionManager.getDeviceSession(deviceId);
+ if (deviceSession != null && deviceSession.supportsLiveCommands()) {
+ deviceSession.sendCommand(command);
} else {
- getDeviceQueue(deviceId).add(command);
+ storage.addObject(QueuedCommand.fromCommand(command), new Request(new Columns.Exclude("id")));
+ broadcastService.updateCommand(true, deviceId);
return false;
}
}
return true;
}
- public Collection<Long> getSupportedCommands(long deviceId) {
- List<Long> result = new ArrayList<>();
- Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
- for (long commandId : getAllDeviceItems(deviceId)) {
- Command command = getById(commandId);
- if (lastPosition != null) {
- BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
- if (command.getTextChannel() && protocol.getSupportedTextCommands().contains(command.getType())
- || !command.getTextChannel()
- && protocol.getSupportedDataCommands().contains(command.getType())) {
- result.add(commandId);
- }
- } else if (command.getType().equals(Command.TYPE_CUSTOM)) {
- result.add(commandId);
- }
- }
- return result;
- }
-
- public Collection<Typed> getCommandTypes(long deviceId, boolean textChannel) {
- Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
- if (lastPosition != null) {
- return getCommandTypes(lastPosition.getProtocol(), textChannel);
- } else {
- return Collections.singletonList(new Typed(Command.TYPE_CUSTOM));
- }
- }
-
- public Collection<Typed> getCommandTypes(String protocolName, boolean textChannel) {
- List<Typed> result = new ArrayList<>();
- BaseProtocol protocol = Context.getServerManager().getProtocol(protocolName);
- Collection<String> commands;
- commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands();
- for (String commandKey : commands) {
- result.add(new Typed(commandKey));
- }
- return result;
- }
-
- public Collection<Typed> getAllCommandTypes() {
- List<Typed> result = new ArrayList<>();
- Field[] fields = Command.class.getDeclaredFields();
- for (Field field : fields) {
- if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
- try {
- result.add(new Typed(field.get(null).toString()));
- } catch (IllegalArgumentException | IllegalAccessException error) {
- LOGGER.warn("Get command types error", error);
- }
- }
- }
- return result;
- }
-
- private Queue<Command> getDeviceQueue(long deviceId) {
- Queue<Command> deviceQueue;
- try {
- readLock();
- deviceQueue = deviceQueues.get(deviceId);
- } finally {
- readUnlock();
- }
- if (deviceQueue != null) {
- return deviceQueue;
- } else {
- try {
- writeLock();
- return deviceQueues.computeIfAbsent(deviceId, key -> new ConcurrentLinkedQueue<>());
- } finally {
- writeUnlock();
- }
- }
- }
-
public Collection<Command> readQueuedCommands(long deviceId) {
return readQueuedCommands(deviceId, Integer.MAX_VALUE);
}
public Collection<Command> readQueuedCommands(long deviceId, int count) {
- Queue<Command> deviceQueue;
try {
- readLock();
- deviceQueue = deviceQueues.get(deviceId);
- } finally {
- readUnlock();
+ var commands = storage.getObjects(QueuedCommand.class, new Request(
+ new Columns.All(),
+ new Condition.Equals("deviceId", deviceId),
+ new Order("id", false, count)));
+ for (var command : commands) {
+ storage.removeObject(QueuedCommand.class, new Request(
+ new Condition.Equals("id", command.getId())));
+ }
+ return commands.stream().map(QueuedCommand::toCommand).collect(Collectors.toList());
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
}
- Collection<Command> result = new ArrayList<>();
- if (deviceQueue != null) {
- Command command = deviceQueue.poll();
- while (command != null && result.size() < count) {
- result.add(command);
- command = deviceQueue.poll();
+ }
+
+ @Override
+ public void updateCommand(boolean local, long deviceId) {
+ if (!local) {
+ DeviceSession deviceSession = connectionManager.getDeviceSession(deviceId);
+ if (deviceSession != null && deviceSession.supportsLiveCommands()) {
+ for (Command command : readQueuedCommands(deviceId)) {
+ deviceSession.sendCommand(command);
+ }
}
}
- return result;
}
}
diff --git a/src/main/java/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java
deleted file mode 100644
index 359061f00..000000000
--- a/src/main/java/org/traccar/database/ConnectionManager.java
+++ /dev/null
@@ -1,216 +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.database;
-
-import io.netty.channel.Channel;
-import io.netty.util.Timeout;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.GlobalTimer;
-import org.traccar.Main;
-import org.traccar.Protocol;
-import org.traccar.config.Keys;
-import org.traccar.handler.events.MotionEventHandler;
-import org.traccar.handler.events.OverspeedEventHandler;
-import org.traccar.model.Device;
-import org.traccar.model.DeviceState;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-import org.traccar.storage.StorageException;
-
-import java.net.SocketAddress;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-public class ConnectionManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class);
-
- private final long deviceTimeout;
- private final boolean updateDeviceState;
-
- private final Map<Long, ActiveDevice> activeDevices = new ConcurrentHashMap<>();
- private final Map<Long, Set<UpdateListener>> listeners = new ConcurrentHashMap<>();
- private final Map<Long, Timeout> timeouts = new ConcurrentHashMap<>();
-
- public ConnectionManager() {
- deviceTimeout = Context.getConfig().getLong(Keys.STATUS_TIMEOUT) * 1000;
- updateDeviceState = Context.getConfig().getBoolean(Keys.STATUS_UPDATE_DEVICE_STATE);
- }
-
- public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
- activeDevices.put(deviceId, new ActiveDevice(deviceId, protocol, channel, remoteAddress));
- }
-
- 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 ActiveDevice getActiveDevice(long deviceId) {
- return activeDevices.get(deviceId);
- }
-
- public void updateDevice(final long deviceId, String status, Date time) {
- Device device = Context.getIdentityManager().getById(deviceId);
- if (device == null) {
- return;
- }
-
- String oldStatus = device.getStatus();
- device.setStatus(status);
-
- if (!status.equals(oldStatus)) {
- String eventType;
- Map<Event, Position> events = new HashMap<>();
- switch (status) {
- case Device.STATUS_ONLINE:
- eventType = Event.TYPE_DEVICE_ONLINE;
- break;
- case Device.STATUS_UNKNOWN:
- eventType = Event.TYPE_DEVICE_UNKNOWN;
- if (updateDeviceState) {
- events.putAll(updateDeviceState(deviceId));
- }
- break;
- default:
- eventType = Event.TYPE_DEVICE_OFFLINE;
- if (updateDeviceState) {
- events.putAll(updateDeviceState(deviceId));
- }
- break;
- }
- events.put(new Event(eventType, deviceId), null);
- Context.getNotificationManager().updateEvents(events);
- }
-
- Timeout timeout = timeouts.remove(deviceId);
- if (timeout != null) {
- timeout.cancel();
- }
-
- if (time != null) {
- device.setLastUpdate(time);
- }
-
- if (status.equals(Device.STATUS_ONLINE)) {
- timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(timeout1 -> {
- if (!timeout1.isCancelled()) {
- updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
- }
- }, deviceTimeout, TimeUnit.MILLISECONDS));
- }
-
- try {
- Context.getDeviceManager().updateDeviceStatus(device);
- } catch (StorageException e) {
- LOGGER.warn("Update device status error", e);
- }
-
- updateDevice(device);
- }
-
- public Map<Event, Position> updateDeviceState(long deviceId) {
- DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
- Map<Event, Position> result = new HashMap<>();
-
- Map<Event, Position> event = Main.getInjector()
- .getInstance(MotionEventHandler.class).updateMotionState(deviceState);
- if (event != null) {
- result.putAll(event);
- }
-
- event = Main.getInjector().getInstance(OverspeedEventHandler.class)
- .updateOverspeedState(deviceState, Context.getDeviceManager().
- lookupAttributeDouble(deviceId, OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT, 0, true, false));
- if (event != null) {
- result.putAll(event);
- }
-
- return result;
- }
-
- public synchronized void sendKeepalive() {
- for (Set<UpdateListener> userListeners : listeners.values()) {
- for (UpdateListener listener : userListeners) {
- listener.onKeepalive();
- }
- }
- }
-
- public synchronized void updateDevice(Device device) {
- for (long userId : Context.getPermissionsManager().getDeviceUsers(device.getId())) {
- if (listeners.containsKey(userId)) {
- for (UpdateListener listener : listeners.get(userId)) {
- listener.onUpdateDevice(device);
- }
- }
- }
- }
-
- public synchronized void updatePosition(Position position) {
- long deviceId = position.getDeviceId();
-
- for (long userId : Context.getPermissionsManager().getDeviceUsers(deviceId)) {
- if (listeners.containsKey(userId)) {
- for (UpdateListener listener : listeners.get(userId)) {
- listener.onUpdatePosition(position);
- }
- }
- }
- }
-
- public synchronized void updateEvent(long userId, Event event) {
- if (listeners.containsKey(userId)) {
- for (UpdateListener listener : listeners.get(userId)) {
- listener.onUpdateEvent(event);
- }
- }
- }
-
- public interface UpdateListener {
- void onKeepalive();
- void onUpdateDevice(Device device);
- void onUpdatePosition(Position position);
- void onUpdateEvent(Event event);
- }
-
- public synchronized void addListener(long userId, UpdateListener listener) {
- if (!listeners.containsKey(userId)) {
- listeners.put(userId, new HashSet<>());
- }
- listeners.get(userId).add(listener);
- }
-
- public synchronized void removeListener(long userId, UpdateListener listener) {
- if (!listeners.containsKey(userId)) {
- listeners.put(userId, new HashSet<>());
- }
- listeners.get(userId).remove(listener);
- }
-
-}
diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java
deleted file mode 100644
index 9ac808a69..000000000
--- a/src/main/java/org/traccar/database/DataManager.java
+++ /dev/null
@@ -1,276 +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.database;
-
-import com.zaxxer.hikari.HikariConfig;
-import com.zaxxer.hikari.HikariDataSource;
-import liquibase.Contexts;
-import liquibase.Liquibase;
-import liquibase.database.Database;
-import liquibase.database.DatabaseFactory;
-import liquibase.exception.LiquibaseException;
-import liquibase.resource.FileSystemResourceAccessor;
-import liquibase.resource.ResourceAccessor;
-import org.traccar.Context;
-import org.traccar.config.Config;
-import org.traccar.config.Keys;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Device;
-import org.traccar.model.Event;
-import org.traccar.model.Permission;
-import org.traccar.model.Position;
-import org.traccar.model.Server;
-import org.traccar.model.Statistics;
-import org.traccar.model.User;
-import org.traccar.storage.DatabaseStorage;
-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.Limit;
-import org.traccar.storage.query.Order;
-import org.traccar.storage.query.Request;
-
-import javax.sql.DataSource;
-import java.io.File;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.Collection;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-public class DataManager {
-
- private final Config config;
-
- private DataSource dataSource;
-
- public DataSource getDataSource() {
- return dataSource;
- }
-
- private final Storage storage;
-
- public Storage getStorage() {
- return storage;
- }
-
- private final boolean forceLdap;
-
- public DataManager(Config config) throws Exception {
- this.config = config;
-
- forceLdap = config.getBoolean(Keys.LDAP_FORCE);
-
- initDatabase();
- initDatabaseSchema();
-
- storage = new DatabaseStorage(dataSource);
- }
-
- private void initDatabase() throws Exception {
-
- String driverFile = config.getString(Keys.DATABASE_DRIVER_FILE);
- if (driverFile != null) {
- ClassLoader classLoader = ClassLoader.getSystemClassLoader();
- try {
- Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class);
- method.setAccessible(true);
- method.invoke(classLoader, new File(driverFile).toURI().toURL());
- } catch (NoSuchMethodException e) {
- Method method = classLoader.getClass()
- .getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
- method.setAccessible(true);
- method.invoke(classLoader, driverFile);
- }
- }
-
- String driver = config.getString(Keys.DATABASE_DRIVER);
- if (driver != null) {
- Class.forName(driver);
- }
-
- HikariConfig hikariConfig = new HikariConfig();
- hikariConfig.setDriverClassName(driver);
- hikariConfig.setJdbcUrl(config.getString(Keys.DATABASE_URL));
- hikariConfig.setUsername(config.getString(Keys.DATABASE_USER));
- hikariConfig.setPassword(config.getString(Keys.DATABASE_PASSWORD));
- hikariConfig.setConnectionInitSql(config.getString(Keys.DATABASE_CHECK_CONNECTION));
- hikariConfig.setIdleTimeout(600000);
-
- int maxPoolSize = config.getInteger(Keys.DATABASE_MAX_POOL_SIZE);
- if (maxPoolSize != 0) {
- hikariConfig.setMaximumPoolSize(maxPoolSize);
- }
-
- dataSource = new HikariDataSource(hikariConfig);
- }
-
- private void initDatabaseSchema() throws LiquibaseException {
-
- if (config.hasKey(Keys.DATABASE_CHANGELOG)) {
-
- ResourceAccessor resourceAccessor = new FileSystemResourceAccessor(new File("."));
-
- Database database = DatabaseFactory.getInstance().openDatabase(
- config.getString(Keys.DATABASE_URL),
- config.getString(Keys.DATABASE_USER),
- config.getString(Keys.DATABASE_PASSWORD),
- config.getString(Keys.DATABASE_DRIVER),
- null, null, null, resourceAccessor);
-
- Liquibase liquibase = new Liquibase(
- config.getString(Keys.DATABASE_CHANGELOG), resourceAccessor, database);
-
- liquibase.clearCheckSums();
-
- liquibase.update(new Contexts());
- }
- }
-
- public User login(String email, String password) throws StorageException {
- User user = storage.getObject(User.class, new Request(
- new Columns.Include("id", "login", "hashedPassword", "salt"),
- new Condition.Or(
- new Condition.Equals("email", "email", email.trim()),
- new Condition.Equals("login", "email"))));
- LdapProvider ldapProvider = Context.getLdapProvider();
- if (user != null) {
- if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password)
- || !forceLdap && user.isPasswordValid(password)) {
- return user;
- }
- } else {
- if (ldapProvider != null && ldapProvider.login(email, password)) {
- user = ldapProvider.getUser(email);
- Context.getUsersManager().addItem(user);
- return user;
- }
- }
- return null;
- }
-
- public void updateUserPassword(User user) throws StorageException {
- storage.updateObject(user, new Request(
- new Columns.Include("hashedPassword", "salt"),
- new Condition.Equals("id", "id")));
- }
-
- public void updateDeviceStatus(Device device) throws StorageException {
- storage.updateObject(device, new Request(
- new Columns.Include("lastUpdate"),
- new Condition.Equals("id", "id")));
- }
-
- public Collection<Position> getPositions(long deviceId, Date from, Date to) throws StorageException {
- return storage.getObjects(Position.class, new Request(
- new Columns.All(),
- new Condition.And(
- new Condition.Equals("deviceId", "deviceId", deviceId),
- new Condition.Between("fixTime", "from", from, "to", to)),
- new Order("fixTime")));
- }
-
- public Position getPrecedingPosition(long deviceId, Date date) throws StorageException {
- return storage.getObject(Position.class, new Request(
- new Columns.All(),
- new Condition.And(
- new Condition.Equals("deviceId", "deviceId", deviceId),
- new Condition.Compare("fixTime", "<=", "time", date)),
- new Order(true, "fixTime"),
- new Limit(1)));
- }
-
- public void updateLatestPosition(Position position) throws StorageException {
- Device device = new Device();
- device.setId(position.getDeviceId());
- device.setPositionId(position.getId());
- storage.updateObject(device, new Request(
- new Columns.Include("positionId"),
- new Condition.Equals("id", "id")));
- }
-
- public Collection<Position> getLatestPositions() throws StorageException {
- List<Position> positions = new LinkedList<>();
- List<Device> devices = storage.getObjects(Device.class, new Request(new Columns.Include("positionId")));
- for (Device device : devices) {
- positions.addAll(storage.getObjects(Position.class, new Request(
- new Columns.All(),
- new Condition.Equals("id", "id", device.getPositionId()))));
- }
- return positions;
- }
-
- public Server getServer() throws StorageException {
- return storage.getObject(Server.class, new Request(new Columns.All()));
- }
-
- public Collection<Event> getEvents(long deviceId, Date from, Date to) throws StorageException {
- return storage.getObjects(Event.class, new Request(
- new Columns.All(),
- new Condition.And(
- new Condition.Equals("deviceId", "deviceId", deviceId),
- new Condition.Between("eventTime", "from", from, "to", to)),
- new Order("eventTime")));
- }
-
- public Collection<Statistics> getStatistics(Date from, Date to) throws StorageException {
- return storage.getObjects(Statistics.class, new Request(
- new Columns.All(),
- new Condition.Between("captureTime", "from", from, "to", to),
- new Order("captureTime")));
- }
-
- public Collection<Permission> getPermissions(Class<? extends BaseModel> owner, Class<? extends BaseModel> property)
- throws StorageException, ClassNotFoundException {
- return storage.getPermissions(owner, property);
- }
-
- public void linkObject(Class<?> owner, long ownerId, Class<?> property, long propertyId, boolean link)
- throws StorageException {
- if (link) {
- storage.addPermission(new Permission(owner, ownerId, property, propertyId));
- } else {
- storage.removePermission(new Permission(owner, ownerId, property, propertyId));
- }
- }
-
- public <T extends BaseModel> T getObject(Class<T> clazz, long entityId) throws StorageException {
- return storage.getObject(clazz, new Request(
- new Columns.All(),
- new Condition.Equals("id", "id", entityId)));
- }
-
- public <T extends BaseModel> Collection<T> getObjects(Class<T> clazz) throws StorageException {
- return storage.getObjects(clazz, new Request(new Columns.All()));
- }
-
- public void addObject(BaseModel entity) throws StorageException {
- entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id"))));
- }
-
- public void updateObject(BaseModel entity) throws StorageException {
- storage.updateObject(entity, new Request(
- new Columns.Exclude("id"),
- new Condition.Equals("id", "id")));
- }
-
- public void removeObject(Class<? extends BaseModel> clazz, long entityId) throws StorageException {
- storage.removeObject(clazz, new Request(new Condition.Equals("id", "id", entityId)));
- }
-
-}
diff --git a/src/main/java/org/traccar/database/DeviceLookupService.java b/src/main/java/org/traccar/database/DeviceLookupService.java
new file mode 100644
index 000000000..6ec6841a1
--- /dev/null
+++ b/src/main/java/org/traccar/database/DeviceLookupService.java
@@ -0,0 +1,141 @@
+/*
+ * 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.database;
+
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+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.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.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+@Singleton
+public class DeviceLookupService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeviceLookupService.class);
+
+ private static final long INFO_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(60);
+ private static final long THROTTLE_MIN_MS = TimeUnit.MINUTES.toMillis(1);
+ private static final long THROTTLE_MAX_MS = TimeUnit.MINUTES.toMillis(30);
+
+ private final Storage storage;
+ private final Timer timer;
+
+ private final boolean throttlingEnabled;
+
+ private static class IdentifierInfo {
+ private long lastQuery;
+ private long delay;
+ private Timeout timeout;
+ }
+
+ private final class IdentifierTask implements TimerTask {
+ private final String uniqueId;
+
+ private IdentifierTask(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public void run(Timeout timeout) {
+ LOGGER.debug("Device lookup expired {}", uniqueId);
+ synchronized (DeviceLookupService.this) {
+ identifierMap.remove(uniqueId);
+ }
+ }
+ }
+
+ private final Map<String, IdentifierInfo> identifierMap = new ConcurrentHashMap<>();
+
+ @Inject
+ public DeviceLookupService(Config config, Storage storage, Timer timer) {
+ this.storage = storage;
+ this.timer = timer;
+ throttlingEnabled = config.getBoolean(Keys.DATABASE_THROTTLE_UNKNOWN);
+ }
+
+ private synchronized boolean isThrottled(String uniqueId) {
+ if (throttlingEnabled) {
+ IdentifierInfo info = identifierMap.get(uniqueId);
+ return info != null && System.currentTimeMillis() < info.lastQuery + info.delay;
+ } else {
+ return false;
+ }
+ }
+
+ private synchronized void lookupSucceeded(String uniqueId) {
+ if (throttlingEnabled) {
+ IdentifierInfo info = identifierMap.remove(uniqueId);
+ if (info != null) {
+ info.timeout.cancel();
+ }
+ }
+ }
+
+ private synchronized void lookupFailed(String uniqueId) {
+ if (throttlingEnabled) {
+ IdentifierInfo info = identifierMap.get(uniqueId);
+ if (info != null) {
+ info.timeout.cancel();
+ info.delay = Math.min(info.delay * 2, THROTTLE_MAX_MS);
+ } else {
+ info = new IdentifierInfo();
+ identifierMap.put(uniqueId, info);
+ info.delay = THROTTLE_MIN_MS;
+ }
+ info.lastQuery = System.currentTimeMillis();
+ info.timeout = timer.newTimeout(new IdentifierTask(uniqueId), INFO_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ LOGGER.debug("Device lookup {} throttled for {} ms", uniqueId, info.delay);
+ }
+ }
+
+ public Device lookup(String[] uniqueIds) {
+ Device device = null;
+ try {
+ for (String uniqueId : uniqueIds) {
+ if (!isThrottled(uniqueId)) {
+ device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("uniqueId", uniqueId)));
+ if (device != null) {
+ lookupSucceeded(uniqueId);
+ break;
+ } else {
+ lookupFailed(uniqueId);
+ }
+ } else {
+ LOGGER.debug("Device lookup throttled {}", uniqueId);
+ }
+ }
+ } catch (StorageException e) {
+ LOGGER.warn("Find device error", e);
+ }
+ return device;
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
deleted file mode 100644
index 40591e869..000000000
--- a/src/main/java/org/traccar/database/DeviceManager.java
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Copyright 2016 - 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.database;
-
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.config.Config;
-import org.traccar.Context;
-import org.traccar.config.Keys;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-import org.traccar.model.DeviceState;
-import org.traccar.model.DeviceAccumulators;
-import org.traccar.model.Group;
-import org.traccar.model.Position;
-import org.traccar.model.Server;
-import org.traccar.storage.StorageException;
-
-public class DeviceManager extends BaseObjectManager<Device> implements IdentityManager, ManagableObjects {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(DeviceManager.class);
-
- private final Config config;
- private final long dataRefreshDelay;
-
- private Map<String, Device> devicesByUniqueId;
- private Map<String, Device> devicesByPhone;
- private final AtomicLong devicesLastUpdate = new AtomicLong();
-
- private final Map<Long, Position> positions = new ConcurrentHashMap<>();
-
- private final Map<Long, DeviceState> deviceStates = new ConcurrentHashMap<>();
-
- public DeviceManager(DataManager dataManager) {
- super(dataManager, Device.class);
- this.config = Context.getConfig();
- try {
- writeLock();
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>();
- }
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>();
- }
- } finally {
- writeUnlock();
- }
- dataRefreshDelay = config.getLong(Keys.DATABASE_REFRESH_DELAY) * 1000;
- refreshLastPositions();
- }
-
- @Override
- public long addUnknownDevice(String uniqueId) {
- Device device = new Device();
- device.setName(uniqueId);
- device.setUniqueId(uniqueId);
- device.setCategory(Context.getConfig().getString(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY));
-
- long defaultGroupId = Context.getConfig().getLong(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID);
- if (defaultGroupId != 0) {
- device.setGroupId(defaultGroupId);
- }
-
- try {
- addItem(device);
-
- LOGGER.info("Automatically registered device " + uniqueId);
-
- if (defaultGroupId != 0) {
- Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
- Context.getPermissionsManager().refreshAllExtendedPermissions();
- }
-
- return device.getId();
- } catch (StorageException e) {
- LOGGER.warn("Automatic device registration error", e);
- return 0;
- }
- }
-
- public void updateDeviceCache(boolean force) {
- long lastUpdate = devicesLastUpdate.get();
- if ((force || System.currentTimeMillis() - lastUpdate > dataRefreshDelay)
- && devicesLastUpdate.compareAndSet(lastUpdate, System.currentTimeMillis())) {
- refreshItems();
- }
- }
-
- @Override
- public Device getByUniqueId(String uniqueId) throws SQLException {
- boolean forceUpdate;
- try {
- readLock();
- forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean(Keys.DATABASE_IGNORE_UNKNOWN);
- } finally {
- readUnlock();
- }
- updateDeviceCache(forceUpdate);
- try {
- readLock();
- return devicesByUniqueId.get(uniqueId);
- } finally {
- readUnlock();
- }
- }
-
- @Override
- public String getDevicePassword(long id, String protocol, String defaultPassword) {
-
- String password = lookupAttributeString(id, Command.KEY_DEVICE_PASSWORD, null, false, false);
- if (password != null) {
- return password;
- }
-
- if (protocol != null) {
- password = Context.getConfig().getString(Keys.PROTOCOL_DEVICE_PASSWORD.withPrefix(protocol));
- if (password != null) {
- return password;
- }
- }
-
- return defaultPassword;
- }
-
- @Override
- public Set<Long> getAllItems() {
- Set<Long> result = super.getAllItems();
- if (result.isEmpty()) {
- updateDeviceCache(true);
- result = super.getAllItems();
- }
- return result;
- }
-
- public Collection<Device> getAllDevices() {
- return getItems(getAllItems());
- }
-
- public Set<Long> getAllUserItems(long userId) {
- return Context.getPermissionsManager().getDevicePermissions(userId);
- }
-
- @Override
- public Set<Long> getUserItems(long userId) {
- if (Context.getPermissionsManager() != null) {
- Set<Long> result = new HashSet<>();
- for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) {
- Device device = getById(deviceId);
- if (device != null && !device.getDisabled()) {
- result.add(deviceId);
- }
- }
- return result;
- } else {
- return new HashSet<>();
- }
- }
-
- public Set<Long> getAllManagedItems(long userId) {
- Set<Long> result = new HashSet<>(getAllUserItems(userId));
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getAllUserItems(managedUserId));
- }
- return result;
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>(getUserItems(userId));
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getUserItems(managedUserId));
- }
- return result;
- }
-
- private void addByUniqueId(Device device) {
- try {
- writeLock();
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>();
- }
- devicesByUniqueId.put(device.getUniqueId(), device);
- } finally {
- writeUnlock();
- }
- }
-
- private void removeByUniqueId(String deviceUniqueId) {
- try {
- writeLock();
- if (devicesByUniqueId != null) {
- devicesByUniqueId.remove(deviceUniqueId);
- }
- } finally {
- writeUnlock();
- }
- }
-
- private void addByPhone(Device device) {
- try {
- writeLock();
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>();
- }
- devicesByPhone.put(device.getPhone(), device);
- } finally {
- writeUnlock();
- }
- }
-
- private void removeByPhone(String phone) {
- if (phone == null || phone.isEmpty()) {
- return;
- }
- try {
- writeLock();
- if (devicesByPhone != null) {
- devicesByPhone.remove(phone);
- }
- } finally {
- writeUnlock();
- }
- }
-
- @Override
- protected void addNewItem(Device device) {
- super.addNewItem(device);
- addByUniqueId(device);
- if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- addByPhone(device);
- }
- if (Context.getGeofenceManager() != null) {
- Position lastPosition = getLastPosition(device.getId());
- if (lastPosition != null) {
- device.setGeofenceIds(Context.getGeofenceManager().getCurrentDeviceGeofences(lastPosition));
- }
- }
- }
-
- @Override
- protected void updateCachedItem(Device device) {
- Device cachedDevice = getById(device.getId());
- cachedDevice.setName(device.getName());
- cachedDevice.setGroupId(device.getGroupId());
- cachedDevice.setCategory(device.getCategory());
- cachedDevice.setContact(device.getContact());
- cachedDevice.setModel(device.getModel());
- cachedDevice.setDisabled(device.getDisabled());
- cachedDevice.setAttributes(device.getAttributes());
- if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) {
- removeByUniqueId(cachedDevice.getUniqueId());
- cachedDevice.setUniqueId(device.getUniqueId());
- addByUniqueId(cachedDevice);
- }
- if (device.getPhone() != null && !device.getPhone().isEmpty()
- && !device.getPhone().equals(cachedDevice.getPhone())) {
- String phone = cachedDevice.getPhone();
- removeByPhone(phone);
- cachedDevice.setPhone(device.getPhone());
- addByPhone(cachedDevice);
- }
- }
-
- @Override
- protected void removeCachedItem(long deviceId) {
- Device cachedDevice = getById(deviceId);
- if (cachedDevice != null) {
- String deviceUniqueId = cachedDevice.getUniqueId();
- String phone = cachedDevice.getPhone();
- super.removeCachedItem(deviceId);
- removeByUniqueId(deviceUniqueId);
- removeByPhone(phone);
- }
- positions.remove(deviceId);
- }
-
- public void updateDeviceStatus(Device device) throws StorageException {
- getDataManager().updateDeviceStatus(device);
- Device cachedDevice = getById(device.getId());
- if (cachedDevice != null) {
- cachedDevice.setStatus(device.getStatus());
- }
- }
-
- private void refreshLastPositions() {
- if (getDataManager() != null) {
- try {
- for (Position position : getDataManager().getLatestPositions()) {
- positions.put(position.getDeviceId(), position);
- }
- } catch (StorageException error) {
- LOGGER.warn("Load latest positions error", error);
- }
- }
- }
-
- public boolean isLatestPosition(Position position) {
- Position lastPosition = getLastPosition(position.getDeviceId());
- return lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) >= 0;
- }
-
- public void updateLatestPosition(Position position) throws StorageException {
-
- if (isLatestPosition(position)) {
-
- getDataManager().updateLatestPosition(position);
-
- Device device = getById(position.getDeviceId());
- if (device != null) {
- device.setPositionId(position.getId());
- }
-
- positions.put(position.getDeviceId(), position);
-
- if (Context.getConnectionManager() != null) {
- Context.getConnectionManager().updatePosition(position);
- }
- }
- }
-
- @Override
- public Position getLastPosition(long deviceId) {
- return positions.get(deviceId);
- }
-
- public Collection<Position> getInitialState(long userId) {
-
- List<Position> result = new LinkedList<>();
-
- if (Context.getPermissionsManager() != null) {
- for (long deviceId : Context.getPermissionsManager().getUserAdmin(userId)
- ? getAllUserItems(userId) : getUserItems(userId)) {
- if (positions.containsKey(deviceId)) {
- result.add(positions.get(deviceId));
- }
- }
- }
-
- return result;
- }
-
- @Override
- public boolean lookupAttributeBoolean(
- long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Boolean.parseBoolean((String) result) : (Boolean) result;
- }
- return defaultValue;
- }
-
- @Override
- public String lookupAttributeString(
- long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- return result != null ? (String) result : defaultValue;
- }
-
- @Override
- public int lookupAttributeInteger(
- long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Integer.parseInt((String) result) : ((Number) result).intValue();
- }
- return defaultValue;
- }
-
- @Override
- public long lookupAttributeLong(
- long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Long.parseLong((String) result) : ((Number) result).longValue();
- }
- return defaultValue;
- }
-
- public double lookupAttributeDouble(
- long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Double.parseDouble((String) result) : ((Number) result).doubleValue();
- }
- return defaultValue;
- }
-
- private Object lookupAttribute(long deviceId, String attributeName, boolean lookupServer, boolean lookupConfig) {
- Object result = null;
- Device device = getById(deviceId);
- if (device != null) {
- result = device.getAttributes().get(attributeName);
- if (result == null) {
- long groupId = device.getGroupId();
- while (groupId != 0) {
- Group group = Context.getGroupsManager().getById(groupId);
- if (group != null) {
- result = group.getAttributes().get(attributeName);
- if (result != null) {
- break;
- }
- groupId = group.getGroupId();
- } else {
- groupId = 0;
- }
- }
- }
- if (result == null && lookupServer) {
- Server server = Context.getPermissionsManager().getServer();
- result = server.getAttributes().get(attributeName);
- }
- if (result == null && lookupConfig) {
- result = Context.getConfig().getString(attributeName);
- }
- }
- return result;
- }
-
- public void resetDeviceAccumulators(DeviceAccumulators deviceAccumulators) throws StorageException {
- Position last = positions.get(deviceAccumulators.getDeviceId());
- if (last != null) {
- if (deviceAccumulators.getTotalDistance() != null) {
- last.getAttributes().put(Position.KEY_TOTAL_DISTANCE, deviceAccumulators.getTotalDistance());
- }
- if (deviceAccumulators.getHours() != null) {
- last.getAttributes().put(Position.KEY_HOURS, deviceAccumulators.getHours());
- }
- getDataManager().addObject(last);
- updateLatestPosition(last);
- } else {
- throw new IllegalArgumentException();
- }
- }
-
- public DeviceState getDeviceState(long deviceId) {
- DeviceState deviceState = deviceStates.get(deviceId);
- if (deviceState == null) {
- deviceState = new DeviceState();
- deviceStates.put(deviceId, deviceState);
- }
- return deviceState;
- }
-
- public void setDeviceState(long deviceId, DeviceState deviceState) {
- deviceStates.put(deviceId, deviceState);
- }
-
-}
diff --git a/src/main/java/org/traccar/database/DriversManager.java b/src/main/java/org/traccar/database/DriversManager.java
deleted file mode 100644
index d111cd643..000000000
--- a/src/main/java/org/traccar/database/DriversManager.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.traccar.model.Driver;
-
-public class DriversManager extends ExtendedObjectManager<Driver> {
-
- private Map<String, Driver> driversByUniqueId;
-
- public DriversManager(DataManager dataManager) {
- super(dataManager, Driver.class);
- try {
- writeLock();
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
- }
- } finally {
- writeUnlock();
- }
- }
-
- private void addByUniqueId(Driver driver) {
- try {
- writeLock();
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
- }
- driversByUniqueId.put(driver.getUniqueId(), driver);
- } finally {
- writeUnlock();
- }
- }
-
- private void removeByUniqueId(String driverUniqueId) {
- try {
- writeLock();
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
- }
- driversByUniqueId.remove(driverUniqueId);
- } finally {
- writeUnlock();
- }
- }
-
- @Override
- protected void addNewItem(Driver driver) {
- super.addNewItem(driver);
- addByUniqueId(driver);
- }
-
- @Override
- protected void updateCachedItem(Driver driver) {
- Driver cachedDriver = getById(driver.getId());
- cachedDriver.setName(driver.getName());
- if (!driver.getUniqueId().equals(cachedDriver.getUniqueId())) {
- removeByUniqueId(cachedDriver.getUniqueId());
- cachedDriver.setUniqueId(driver.getUniqueId());
- addByUniqueId(cachedDriver);
- }
- cachedDriver.setAttributes(driver.getAttributes());
- }
-
- @Override
- protected void removeCachedItem(long driverId) {
- Driver cachedDriver = getById(driverId);
- if (cachedDriver != null) {
- String driverUniqueId = cachedDriver.getUniqueId();
- super.removeCachedItem(driverId);
- removeByUniqueId(driverUniqueId);
- }
- }
-
- public Driver getDriverByUniqueId(String uniqueId) {
- try {
- readLock();
- return driversByUniqueId.get(uniqueId);
- } finally {
- readUnlock();
- }
- }
-}
diff --git a/src/main/java/org/traccar/database/ExtendedObjectManager.java b/src/main/java/org/traccar/database/ExtendedObjectManager.java
deleted file mode 100644
index 006ed47b2..000000000
--- a/src/main/java/org/traccar/database/ExtendedObjectManager.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Collection;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.model.Device;
-import org.traccar.model.Group;
-import org.traccar.model.Permission;
-import org.traccar.model.BaseModel;
-import org.traccar.storage.StorageException;
-
-public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleObjectManager<T> {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ExtendedObjectManager.class);
-
- private final Map<Long, Set<Long>> deviceItems = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> deviceItemsWithGroups = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> groupItems = new ConcurrentHashMap<>();
-
- protected ExtendedObjectManager(DataManager dataManager, Class<T> baseClass) {
- super(dataManager, baseClass);
- refreshExtendedPermissions();
- }
-
- public final Set<Long> getGroupItems(long groupId) {
- try {
- readLock();
- Set<Long> result = groupItems.get(groupId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- public final Set<Long> getDeviceItems(long deviceId) {
- try {
- readLock();
- Set<Long> result = deviceItems.get(deviceId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getAllDeviceItems(long deviceId) {
- try {
- readLock();
- Set<Long> result = deviceItemsWithGroups.get(deviceId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- @Override
- public void removeItem(long itemId) throws StorageException {
- super.removeItem(itemId);
- refreshExtendedPermissions();
- }
-
- public void refreshExtendedPermissions() {
- if (getDataManager() != null) {
- try {
- Collection<Permission> databaseGroupPermissions =
- getDataManager().getPermissions(Group.class, getBaseClass());
-
- Collection<Permission> databaseDevicePermissions =
- getDataManager().getPermissions(Device.class, getBaseClass());
-
- writeLock();
-
- groupItems.clear();
- deviceItems.clear();
- deviceItemsWithGroups.clear();
-
- for (Permission groupPermission : databaseGroupPermissions) {
- groupItems
- .computeIfAbsent(groupPermission.getOwnerId(), key -> new HashSet<>())
- .add(groupPermission.getPropertyId());
- }
-
- for (Permission devicePermission : databaseDevicePermissions) {
- deviceItems
- .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
- .add(devicePermission.getPropertyId());
- deviceItemsWithGroups
- .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
- .add(devicePermission.getPropertyId());
- }
-
- for (Device device : Context.getDeviceManager().getAllDevices()) {
- long groupId = device.getGroupId();
- while (groupId > 0) {
- deviceItemsWithGroups
- .computeIfAbsent(device.getId(), key -> new HashSet<>())
- .addAll(groupItems.getOrDefault(groupId, new HashSet<>()));
- Group group = Context.getGroupsManager().getById(groupId);
- groupId = group != null ? group.getGroupId() : 0;
- }
- }
-
- } catch (StorageException | ClassNotFoundException error) {
- LOGGER.warn("Refresh permissions error", error);
- } finally {
- writeUnlock();
- }
- }
- }
-}
diff --git a/src/main/java/org/traccar/database/GeofenceManager.java b/src/main/java/org/traccar/database/GeofenceManager.java
deleted file mode 100644
index a32847cf9..000000000
--- a/src/main/java/org/traccar/database/GeofenceManager.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2016 - 2017 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 java.util.ArrayList;
-import java.util.List;
-
-import org.traccar.Context;
-import org.traccar.model.Device;
-import org.traccar.model.Geofence;
-import org.traccar.model.Position;
-
-public class GeofenceManager extends ExtendedObjectManager<Geofence> {
-
- public GeofenceManager(DataManager dataManager) {
- super(dataManager, Geofence.class);
- }
-
- @Override
- public final void refreshExtendedPermissions() {
- super.refreshExtendedPermissions();
- recalculateDevicesGeofences();
- }
-
- public List<Long> getCurrentDeviceGeofences(Position position) {
- List<Long> result = new ArrayList<>();
- for (long geofenceId : getAllDeviceItems(position.getDeviceId())) {
- Geofence geofence = getById(geofenceId);
- if (geofence != null && geofence.getGeometry()
- .containsPoint(position.getLatitude(), position.getLongitude())) {
- result.add(geofenceId);
- }
- }
- return result;
- }
-
- public void recalculateDevicesGeofences() {
- for (Device device : Context.getDeviceManager().getAllDevices()) {
- List<Long> deviceGeofenceIds = device.getGeofenceIds();
- if (deviceGeofenceIds == null) {
- deviceGeofenceIds = new ArrayList<>();
- } else {
- deviceGeofenceIds.clear();
- }
- Position lastPosition = Context.getIdentityManager().getLastPosition(device.getId());
- if (lastPosition != null && getAllDeviceItems(device.getId()) != null) {
- deviceGeofenceIds.addAll(getCurrentDeviceGeofences(lastPosition));
- }
- device.setGeofenceIds(deviceGeofenceIds);
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/database/GroupsManager.java b/src/main/java/org/traccar/database/GroupsManager.java
deleted file mode 100644
index dafddc0cc..000000000
--- a/src/main/java/org/traccar/database/GroupsManager.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.HashSet;
-import java.util.Set;
-
-import org.traccar.Context;
-import org.traccar.model.Group;
-import org.traccar.storage.StorageException;
-
-public class GroupsManager extends BaseObjectManager<Group> implements ManagableObjects {
-
- public GroupsManager(DataManager dataManager) {
- super(dataManager, Group.class);
- }
-
- private void checkGroupCycles(Group group) {
- Set<Long> groups = new HashSet<>();
- while (group != null) {
- if (groups.contains(group.getId())) {
- throw new IllegalArgumentException("Cycle in group hierarchy");
- }
- groups.add(group.getId());
- group = getById(group.getGroupId());
- }
- }
-
- @Override
- public Set<Long> getAllItems() {
- Set<Long> result = super.getAllItems();
- if (result.isEmpty()) {
- refreshItems();
- result = super.getAllItems();
- }
- return result;
- }
-
- @Override
- protected void addNewItem(Group group) {
- checkGroupCycles(group);
- super.addNewItem(group);
- }
-
- @Override
- public void updateItem(Group group) throws StorageException {
- checkGroupCycles(group);
- super.updateItem(group);
- }
-
- @Override
- public Set<Long> getUserItems(long userId) {
- if (Context.getPermissionsManager() != null) {
- return Context.getPermissionsManager().getGroupPermissions(userId);
- } else {
- return new HashSet<>();
- }
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = getUserItems(userId);
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getUserItems(managedUserId));
- }
- return result;
- }
-
-}
diff --git a/src/main/java/org/traccar/database/IdentityManager.java b/src/main/java/org/traccar/database/IdentityManager.java
deleted file mode 100644
index af6a6ce71..000000000
--- a/src/main/java/org/traccar/database/IdentityManager.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2015 - 2019 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.Position;
-
-public interface IdentityManager {
-
- long addUnknownDevice(String uniqueId);
-
- Device getById(long id);
-
- Device getByUniqueId(String uniqueId) throws Exception;
-
- String getDevicePassword(long id, String protocol, String defaultPassword);
-
- Position getLastPosition(long deviceId);
-
- boolean isLatestPosition(Position position);
-
- boolean lookupAttributeBoolean(
- long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig);
-
- String lookupAttributeString(
- long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig);
-
- int lookupAttributeInteger(
- long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig);
-
- long lookupAttributeLong(
- long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig);
-
- double lookupAttributeDouble(
- long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig);
-
-}
diff --git a/src/main/java/org/traccar/database/LdapProvider.java b/src/main/java/org/traccar/database/LdapProvider.java
index d659a11a1..d517294b8 100644
--- a/src/main/java/org/traccar/database/LdapProvider.java
+++ b/src/main/java/org/traccar/database/LdapProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
diff --git a/src/main/java/org/traccar/database/MailManager.java b/src/main/java/org/traccar/database/MailManager.java
deleted file mode 100644
index d94f55cda..000000000
--- a/src/main/java/org/traccar/database/MailManager.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.Main;
-import org.traccar.model.User;
-import org.traccar.notification.PropertiesProvider;
-
-import javax.mail.BodyPart;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.Multipart;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeBodyPart;
-import javax.mail.internet.MimeMessage;
-import javax.mail.internet.MimeMultipart;
-import java.util.Date;
-import java.util.Properties;
-
-public final class MailManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MailManager.class);
-
- private static Properties getProperties(PropertiesProvider provider) {
- Properties properties = new Properties();
- String host = provider.getString("mail.smtp.host");
- if (host != null) {
- properties.put("mail.transport.protocol", provider.getString("mail.transport.protocol", "smtp"));
- properties.put("mail.smtp.host", host);
- properties.put("mail.smtp.port", String.valueOf(provider.getInteger("mail.smtp.port", 25)));
-
- Boolean starttlsEnable = provider.getBoolean("mail.smtp.starttls.enable");
- if (starttlsEnable != null) {
- properties.put("mail.smtp.starttls.enable", String.valueOf(starttlsEnable));
- }
- Boolean starttlsRequired = provider.getBoolean("mail.smtp.starttls.required");
- if (starttlsRequired != null) {
- properties.put("mail.smtp.starttls.required", String.valueOf(starttlsRequired));
- }
-
- Boolean sslEnable = provider.getBoolean("mail.smtp.ssl.enable");
- if (sslEnable != null) {
- properties.put("mail.smtp.ssl.enable", String.valueOf(sslEnable));
- }
- String sslTrust = provider.getString("mail.smtp.ssl.trust");
- if (sslTrust != null) {
- properties.put("mail.smtp.ssl.trust", sslTrust);
- }
-
- String sslProtocols = provider.getString("mail.smtp.ssl.protocols");
- if (sslProtocols != null) {
- properties.put("mail.smtp.ssl.protocols", sslProtocols);
- }
-
- String username = provider.getString("mail.smtp.username");
- if (username != null) {
- properties.put("mail.smtp.username", username);
- }
- String password = provider.getString("mail.smtp.password");
- if (password != null) {
- properties.put("mail.smtp.password", password);
- }
- String from = provider.getString("mail.smtp.from");
- if (from != null) {
- properties.put("mail.smtp.from", from);
- }
- }
- return properties;
- }
-
- public boolean getEmailEnabled() {
- return Context.getConfig().hasKey("mail.smtp.host");
- }
-
- public void sendMessage(
- long userId, String subject, String body) throws MessagingException {
- sendMessage(userId, subject, body, null);
- }
-
- public void sendMessage(
- long userId, String subject, String body, MimeBodyPart attachment) throws MessagingException {
- User user = Context.getPermissionsManager().getUser(userId);
-
- Properties properties = null;
- if (!Context.getConfig().getBoolean("mail.smtp.ignoreUserConfig")) {
- properties = getProperties(new PropertiesProvider(user));
- }
- if (properties == null || !properties.containsKey("mail.smtp.host")) {
- properties = getProperties(new PropertiesProvider(Context.getConfig()));
- }
- if (!properties.containsKey("mail.smtp.host")) {
- LOGGER.warn("No SMTP configuration found");
- return;
- }
-
- Session session = Session.getInstance(properties);
-
- MimeMessage message = new MimeMessage(session);
-
- String from = properties.getProperty("mail.smtp.from");
- if (from != null) {
- message.setFrom(new InternetAddress(from));
- }
-
- message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
- message.setSubject(subject);
- message.setSentDate(new Date());
-
- if (attachment != null) {
- Multipart multipart = new MimeMultipart();
-
- BodyPart messageBodyPart = new MimeBodyPart();
- messageBodyPart.setContent(body, "text/html; charset=utf-8");
- multipart.addBodyPart(messageBodyPart);
- multipart.addBodyPart(attachment);
-
- message.setContent(multipart);
- } else {
- message.setContent(body, "text/html; charset=utf-8");
- }
-
- try (Transport transport = session.getTransport()) {
- Main.getInjector().getInstance(StatisticsManager.class).registerMail();
- transport.connect(
- properties.getProperty("mail.smtp.host"),
- properties.getProperty("mail.smtp.username"),
- properties.getProperty("mail.smtp.password"));
- transport.sendMessage(message, message.getAllRecipients());
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/database/MaintenancesManager.java b/src/main/java/org/traccar/database/MaintenancesManager.java
deleted file mode 100644
index 4e266cb78..000000000
--- a/src/main/java/org/traccar/database/MaintenancesManager.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
- * Copyright 2018 Andrey Kunitsyn (andrey@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.Maintenance;
-
-public class MaintenancesManager extends ExtendedObjectManager<Maintenance> {
-
- public MaintenancesManager(DataManager dataManager) {
- super(dataManager, Maintenance.class);
- }
-
-}
diff --git a/src/main/java/org/traccar/database/ManagableObjects.java b/src/main/java/org/traccar/database/ManagableObjects.java
deleted file mode 100644
index ec9549493..000000000
--- a/src/main/java/org/traccar/database/ManagableObjects.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Set;
-
-public interface ManagableObjects {
-
- Set<Long> getUserItems(long userId);
-
- Set<Long> getManagedItems(long userId);
-
-}
diff --git a/src/main/java/org/traccar/database/MediaManager.java b/src/main/java/org/traccar/database/MediaManager.java
index edade5766..2f2369c96 100644
--- a/src/main/java/org/traccar/database/MediaManager.java
+++ b/src/main/java/org/traccar/database/MediaManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -18,10 +18,15 @@ package org.traccar.database;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
@@ -30,14 +35,16 @@ import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
+@Singleton
public class MediaManager {
private static final Logger LOGGER = LoggerFactory.getLogger(MediaManager.class);
- private String path;
+ private final String path;
- public MediaManager(String path) {
- this.path = path;
+ @Inject
+ public MediaManager(Config config) {
+ this.path = config.getString(Keys.MEDIA_PATH);
}
private File createFile(String uniqueId, String name) throws IOException {
@@ -49,6 +56,10 @@ public class MediaManager {
return filePath.toFile();
}
+ public OutputStream createFileStream(String uniqueId, String name, String extension) throws IOException {
+ return new FileOutputStream(createFile(uniqueId, name + "." + extension));
+ }
+
public String writeFile(String uniqueId, ByteBuf buf, String extension) {
if (path != null) {
int size = buf.readableBytes();
diff --git a/src/main/java/org/traccar/database/NotificationManager.java b/src/main/java/org/traccar/database/NotificationManager.java
index f358b1d4d..3a57788fb 100644
--- a/src/main/java/org/traccar/database/NotificationManager.java
+++ b/src/main/java/org/traccar/database/NotificationManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,128 +16,141 @@
*/
package org.traccar.database;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.forward.EventData;
+import org.traccar.forward.EventForwarder;
+import org.traccar.geocoder.Geocoder;
import org.traccar.model.Calendar;
+import org.traccar.model.Device;
import org.traccar.model.Event;
+import org.traccar.model.Geofence;
+import org.traccar.model.Maintenance;
import org.traccar.model.Notification;
import org.traccar.model.Position;
-import org.traccar.model.Typed;
+import org.traccar.notification.MessageException;
+import org.traccar.notification.NotificatorManager;
+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.Request;
+
+import jakarta.annotation.Nullable;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
-public class NotificationManager extends ExtendedObjectManager<Notification> {
+@Singleton
+public class NotificationManager {
private static final Logger LOGGER = LoggerFactory.getLogger(NotificationManager.class);
- private final boolean geocodeOnRequest;
+ private final Storage storage;
+ private final CacheManager cacheManager;
+ private final EventForwarder eventForwarder;
+ private final NotificatorManager notificatorManager;
+ private final Geocoder geocoder;
- public NotificationManager(DataManager dataManager) {
- super(dataManager, Notification.class);
- geocodeOnRequest = Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST);
- }
+ private final boolean geocodeOnRequest;
- private Set<Long> getEffectiveNotifications(long userId, long deviceId, Date time) {
- Set<Long> result = new HashSet<>();
- Set<Long> deviceNotifications = getAllDeviceItems(deviceId);
- for (long itemId : getUserItems(userId)) {
- if (getById(itemId).getAlways() || deviceNotifications.contains(itemId)) {
- long calendarId = getById(itemId).getCalendarId();
- Calendar calendar = calendarId != 0 ? Context.getCalendarManager().getById(calendarId) : null;
- if (calendar == null || calendar.checkMoment(time)) {
- result.add(itemId);
- }
- }
- }
- return result;
+ @Inject
+ public NotificationManager(
+ Config config, Storage storage, CacheManager cacheManager, @Nullable EventForwarder eventForwarder,
+ NotificatorManager notificatorManager, @Nullable Geocoder geocoder) {
+ this.storage = storage;
+ this.cacheManager = cacheManager;
+ this.eventForwarder = eventForwarder;
+ this.notificatorManager = notificatorManager;
+ this.geocoder = geocoder;
+ geocodeOnRequest = config.getBoolean(Keys.GEOCODER_ON_REQUEST);
}
- public void updateEvent(Event event, Position position) {
+ private void updateEvent(Event event, Position position) {
try {
- getDataManager().addObject(event);
+ event.setId(storage.addObject(event, new Request(new Columns.Exclude("id"))));
} catch (StorageException error) {
LOGGER.warn("Event save error", error);
}
- long deviceId = event.getDeviceId();
- Set<Long> users = Context.getPermissionsManager().getDeviceUsers(deviceId);
- Set<Long> usersToForward = null;
- if (Context.getEventForwarder() != null) {
- usersToForward = new HashSet<>();
- }
- for (long userId : users) {
- if ((event.getGeofenceId() == 0
- || Context.getGeofenceManager().checkItemPermission(userId, event.getGeofenceId()))
- && (event.getMaintenanceId() == 0
- || Context.getMaintenancesManager().checkItemPermission(userId, event.getMaintenanceId()))) {
- if (usersToForward != null) {
- usersToForward.add(userId);
- }
- final Set<String> notificators = new HashSet<>();
- for (long notificationId : getEffectiveNotifications(userId, deviceId, event.getEventTime())) {
- Notification notification = getById(notificationId);
- if (getById(notificationId).getType().equals(event.getType())) {
- boolean filter = false;
- if (event.getType().equals(Event.TYPE_ALARM)) {
- String alarmsAttribute = notification.getString("alarms");
- if (alarmsAttribute == null) {
- filter = true;
- } else {
- List<String> alarms = Arrays.asList(alarmsAttribute.split(","));
- filter = !alarms.contains(event.getString(Position.KEY_ALARM));
- }
- }
- if (!filter) {
- notificators.addAll(notification.getNotificatorsTypes());
+ var notifications = cacheManager.getDeviceObjects(event.getDeviceId(), Notification.class).stream()
+ .filter(notification -> notification.getType().equals(event.getType()))
+ .filter(notification -> {
+ if (event.getType().equals(Event.TYPE_ALARM)) {
+ String alarmsAttribute = notification.getString("alarms");
+ if (alarmsAttribute != null) {
+ return Arrays.asList(alarmsAttribute.split(","))
+ .contains(event.getString(Position.KEY_ALARM));
}
+ return false;
}
- }
-
- if (position != null && position.getAddress() == null
- && geocodeOnRequest && Context.getGeocoder() != null) {
- position.setAddress(Context.getGeocoder()
- .getAddress(position.getLatitude(), position.getLongitude(), null));
- }
+ return true;
+ })
+ .filter(notification -> {
+ long calendarId = notification.getCalendarId();
+ Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null;
+ return calendar == null || calendar.checkMoment(event.getEventTime());
+ })
+ .collect(Collectors.toUnmodifiableList());
- for (String notificator : notificators) {
- Context.getNotificatorManager().getNotificator(notificator).sendAsync(userId, event, position);
- }
+ if (!notifications.isEmpty()) {
+ if (position != null && position.getAddress() == null && geocodeOnRequest && geocoder != null) {
+ position.setAddress(geocoder.getAddress(position.getLatitude(), position.getLongitude(), null));
}
+
+ notifications.forEach(notification -> {
+ cacheManager.getNotificationUsers(notification.getId(), event.getDeviceId()).forEach(user -> {
+ for (String notificator : notification.getNotificatorsTypes()) {
+ try {
+ notificatorManager.getNotificator(notificator).send(notification, user, event, position);
+ } catch (MessageException exception) {
+ LOGGER.warn("Notification failed", exception);
+ }
+ }
+ });
+ });
}
- if (Context.getEventForwarder() != null) {
- Context.getEventForwarder().forwardEvent(event, position, usersToForward);
- }
+
+ forwardEvent(event, position);
}
- public void updateEvents(Map<Event, Position> events) {
- for (Entry<Event, Position> event : events.entrySet()) {
- updateEvent(event.getKey(), event.getValue());
+ private void forwardEvent(Event event, Position position) {
+ if (eventForwarder != null) {
+ EventData eventData = new EventData();
+ eventData.setEvent(event);
+ eventData.setPosition(position);
+ eventData.setDevice(cacheManager.getObject(Device.class, event.getDeviceId()));
+ if (event.getGeofenceId() != 0) {
+ eventData.setGeofence(cacheManager.getObject(Geofence.class, event.getGeofenceId()));
+ }
+ if (event.getMaintenanceId() != 0) {
+ eventData.setMaintenance(cacheManager.getObject(Maintenance.class, event.getMaintenanceId()));
+ }
+ eventForwarder.forward(eventData, (success, throwable) -> {
+ if (!success) {
+ LOGGER.warn("Event forwarding failed", throwable);
+ }
+ });
}
}
- public Set<Typed> getAllNotificationTypes() {
- Set<Typed> types = new HashSet<>();
- Field[] fields = Event.class.getDeclaredFields();
- for (Field field : fields) {
- if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
- try {
- types.add(new Typed(field.get(null).toString()));
- } catch (IllegalArgumentException | IllegalAccessException error) {
- LOGGER.warn("Get event types error", error);
- }
+ public void updateEvents(Map<Event, Position> events) {
+ for (Entry<Event, Position> entry : events.entrySet()) {
+ Event event = entry.getKey();
+ Position position = entry.getValue();
+ try {
+ cacheManager.addDevice(event.getDeviceId());
+ updateEvent(event, position);
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ } finally {
+ cacheManager.removeDevice(event.getDeviceId());
}
}
- return types;
}
}
diff --git a/src/main/java/org/traccar/database/OpenIdProvider.java b/src/main/java/org/traccar/database/OpenIdProvider.java
new file mode 100644
index 000000000..312be8890
--- /dev/null
+++ b/src/main/java/org/traccar/database/OpenIdProvider.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2023 Daniel Raper (me@danr.uk)
+ *
+ * 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.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.api.resource.SessionResource;
+import org.traccar.api.security.LoginService;
+import org.traccar.model.User;
+import org.traccar.storage.StorageException;
+import org.traccar.helper.LogAction;
+import org.traccar.helper.WebHelper;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.security.GeneralSecurityException;
+import java.util.List;
+import java.util.Map;
+import java.io.IOException;
+import jakarta.servlet.http.HttpServletRequest;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.inject.Inject;
+
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
+import com.nimbusds.oauth2.sdk.AuthorizationCode;
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.AuthorizationGrant;
+import com.nimbusds.oauth2.sdk.TokenRequest;
+import com.nimbusds.oauth2.sdk.TokenResponse;
+import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.AuthorizationResponse;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
+import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser;
+import com.nimbusds.openid.connect.sdk.UserInfoResponse;
+import com.nimbusds.openid.connect.sdk.UserInfoRequest;
+import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+import com.nimbusds.openid.connect.sdk.claims.UserInfo;
+
+public class OpenIdProvider {
+ private final Boolean force;
+ private final ClientID clientId;
+ private final ClientAuthentication clientAuth;
+ private URI callbackUrl;
+ private URI authUrl;
+ private URI tokenUrl;
+ private URI userInfoUrl;
+ private URI baseUrl;
+ private final String adminGroup;
+ private final String allowGroup;
+
+ private LoginService loginService;
+
+ @Inject
+ public OpenIdProvider(
+ Config config, LoginService loginService, HttpClient httpClient, ObjectMapper objectMapper
+ ) throws InterruptedException, IOException, URISyntaxException {
+ this.loginService = loginService;
+
+ force = config.getBoolean(Keys.OPENID_FORCE);
+ clientId = new ClientID(config.getString(Keys.OPENID_CLIENT_ID));
+ clientAuth = new ClientSecretBasic(clientId, new Secret(config.getString(Keys.OPENID_CLIENT_SECRET)));
+
+ baseUrl = new URI(WebHelper.retrieveWebUrl(config));
+ callbackUrl = new URI(WebHelper.retrieveWebUrl(config) + "/api/session/openid/callback");
+
+ if (config.hasKey(Keys.OPENID_ISSUER_URL)) {
+ HttpRequest httpRequest = HttpRequest.newBuilder(
+ URI.create(config.getString(Keys.OPENID_ISSUER_URL) + "/.well-known/openid-configuration"))
+ .header("Accept", "application/json")
+ .build();
+
+ String httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()).body();
+
+ Map<String, Object> discoveryMap = objectMapper.readValue(
+ httpResponse, new TypeReference<Map<String, Object>>() { });
+
+ authUrl = new URI((String) discoveryMap.get("authorization_endpoint"));
+ tokenUrl = new URI((String) discoveryMap.get("token_endpoint"));
+ userInfoUrl = new URI((String) discoveryMap.get("userinfo_endpoint"));
+ } else {
+ authUrl = new URI(config.getString(Keys.OPENID_AUTH_URL));
+ tokenUrl = new URI(config.getString(Keys.OPENID_TOKEN_URL));
+ userInfoUrl = new URI(config.getString(Keys.OPENID_USERINFO_URL));
+ }
+
+ adminGroup = config.getString(Keys.OPENID_ADMIN_GROUP);
+ allowGroup = config.getString(Keys.OPENID_ALLOW_GROUP);
+ }
+
+ public URI createAuthUri() {
+ Scope scope = new Scope("openid", "profile", "email");
+
+ if (adminGroup != null) {
+ scope.add("groups");
+ }
+
+ AuthenticationRequest.Builder request = new AuthenticationRequest.Builder(
+ new ResponseType("code"),
+ scope,
+ clientId,
+ callbackUrl);
+
+ return request.endpointURI(authUrl)
+ .state(new State())
+ .build()
+ .toURI();
+ }
+
+ private OIDCTokenResponse getToken(
+ AuthorizationCode code) throws IOException, ParseException, GeneralSecurityException {
+ AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, callbackUrl);
+ TokenRequest tokenRequest = new TokenRequest(tokenUrl, clientAuth, codeGrant);
+
+ HTTPResponse tokenResponse = tokenRequest.toHTTPRequest().send();
+ TokenResponse token = OIDCTokenResponseParser.parse(tokenResponse);
+ if (!token.indicatesSuccess()) {
+ throw new GeneralSecurityException("Unable to authenticate with the OpenID Connect provider.");
+ }
+
+ return (OIDCTokenResponse) token.toSuccessResponse();
+ }
+
+ private UserInfo getUserInfo(BearerAccessToken token) throws IOException, ParseException, GeneralSecurityException {
+ HTTPResponse httpResponse = new UserInfoRequest(userInfoUrl, token)
+ .toHTTPRequest()
+ .send();
+
+ UserInfoResponse userInfoResponse = UserInfoResponse.parse(httpResponse);
+
+ if (!userInfoResponse.indicatesSuccess()) {
+ throw new GeneralSecurityException(
+ "Failed to access OpenID Connect user info endpoint. Please contact your administrator.");
+ }
+
+ return userInfoResponse.toSuccessResponse().getUserInfo();
+ }
+
+ public URI handleCallback(
+ URI requestUri, HttpServletRequest request
+ ) throws StorageException, ParseException, IOException, GeneralSecurityException {
+ AuthorizationResponse response = AuthorizationResponse.parse(requestUri);
+
+ if (!response.indicatesSuccess()) {
+ throw new GeneralSecurityException(response.toErrorResponse().getErrorObject().getDescription());
+ }
+
+ AuthorizationCode authCode = response.toSuccessResponse().getAuthorizationCode();
+
+ if (authCode == null) {
+ throw new GeneralSecurityException("Malformed OpenID callback.");
+ }
+
+ OIDCTokenResponse tokens = getToken(authCode);
+
+ BearerAccessToken bearerToken = tokens.getOIDCTokens().getBearerAccessToken();
+
+ UserInfo userInfo = getUserInfo(bearerToken);
+
+ List<String> userGroups = userInfo.getStringListClaim("groups");
+ Boolean administrator = adminGroup != null && userGroups.contains(adminGroup);
+
+ if (!(administrator || allowGroup == null || userGroups.contains(allowGroup))) {
+ throw new GeneralSecurityException("Your OpenID Groups do not permit access to Traccar.");
+ }
+
+ User user = loginService.login(userInfo.getEmailAddress(), userInfo.getName(), administrator);
+
+ request.getSession().setAttribute(SessionResource.USER_ID_KEY, user.getId());
+ LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request));
+
+ return baseUrl;
+ }
+
+ public boolean getForce() {
+ return force;
+ }
+}
diff --git a/src/main/java/org/traccar/database/OrderManager.java b/src/main/java/org/traccar/database/OrderManager.java
deleted file mode 100644
index c3253e52f..000000000
--- a/src/main/java/org/traccar/database/OrderManager.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2021 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.Order;
-
-public class OrderManager extends ExtendedObjectManager<Order> {
-
- public OrderManager(DataManager dataManager) {
- super(dataManager, Order.class);
- }
-
-}
diff --git a/src/main/java/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java
deleted file mode 100644
index 2bb808033..000000000
--- a/src/main/java/org/traccar/database/PermissionsManager.java
+++ /dev/null
@@ -1,528 +0,0 @@
-/*
- * Copyright 2015 - 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.database;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.model.Attribute;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Calendar;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-import org.traccar.model.Driver;
-import org.traccar.model.Geofence;
-import org.traccar.model.Group;
-import org.traccar.model.Maintenance;
-import org.traccar.model.ManagedUser;
-import org.traccar.model.Notification;
-import org.traccar.model.Order;
-import org.traccar.model.Permission;
-import org.traccar.model.Server;
-import org.traccar.model.User;
-import org.traccar.storage.StorageException;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-public class PermissionsManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsManager.class);
-
- private final DataManager dataManager;
- private final UsersManager usersManager;
-
- private volatile Server server;
-
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- private final Map<Long, Set<Long>> groupPermissions = new HashMap<>();
- private final Map<Long, Set<Long>> devicePermissions = new HashMap<>();
- private final Map<Long, Set<Long>> deviceUsers = new HashMap<>();
- private final Map<Long, Set<Long>> groupDevices = new HashMap<>();
-
- public PermissionsManager(DataManager dataManager, UsersManager usersManager) {
- this.dataManager = dataManager;
- this.usersManager = usersManager;
- refreshServer();
- refreshDeviceAndGroupPermissions();
- }
-
- protected final void readLock() {
- lock.readLock().lock();
- }
-
- protected final void readUnlock() {
- lock.readLock().unlock();
- }
-
- protected final void writeLock() {
- lock.writeLock().lock();
- }
-
- protected final void writeUnlock() {
- lock.writeLock().unlock();
- }
-
- public User getUser(long userId) {
- readLock();
- try {
- return usersManager.getById(userId);
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getGroupPermissions(long userId) {
- readLock();
- try {
- if (!groupPermissions.containsKey(userId)) {
- groupPermissions.put(userId, new HashSet<>());
- }
- return groupPermissions.get(userId);
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getDevicePermissions(long userId) {
- readLock();
- try {
- if (!devicePermissions.containsKey(userId)) {
- devicePermissions.put(userId, new HashSet<>());
- }
- return devicePermissions.get(userId);
- } finally {
- readUnlock();
- }
- }
-
- private Set<Long> getAllDeviceUsers(long deviceId) {
- readLock();
- try {
- if (!deviceUsers.containsKey(deviceId)) {
- deviceUsers.put(deviceId, new HashSet<>());
- }
- return deviceUsers.get(deviceId);
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getDeviceUsers(long deviceId) {
- Device device = Context.getIdentityManager().getById(deviceId);
- if (device != null && !device.getDisabled()) {
- return getAllDeviceUsers(deviceId);
- } else {
- Set<Long> result = new HashSet<>();
- for (long userId : getAllDeviceUsers(deviceId)) {
- if (getUserAdmin(userId)) {
- result.add(userId);
- }
- }
- return result;
- }
- }
-
- public Set<Long> getGroupDevices(long groupId) {
- readLock();
- try {
- if (!groupDevices.containsKey(groupId)) {
- groupDevices.put(groupId, new HashSet<>());
- }
- return groupDevices.get(groupId);
- } finally {
- readUnlock();
- }
- }
-
- public void refreshServer() {
- try {
- server = dataManager.getServer();
- } catch (StorageException error) {
- LOGGER.warn("Refresh server config error", error);
- }
- }
-
- public final void refreshDeviceAndGroupPermissions() {
- writeLock();
- try {
- groupPermissions.clear();
- devicePermissions.clear();
- try {
- GroupTree groupTree = new GroupTree(Context.getGroupsManager().getItems(
- Context.getGroupsManager().getAllItems()),
- Context.getDeviceManager().getAllDevices());
- for (Permission groupPermission : dataManager.getPermissions(User.class, Group.class)) {
- Set<Long> userGroupPermissions = getGroupPermissions(groupPermission.getOwnerId());
- Set<Long> userDevicePermissions = getDevicePermissions(groupPermission.getOwnerId());
- userGroupPermissions.add(groupPermission.getPropertyId());
- for (Group group : groupTree.getGroups(groupPermission.getPropertyId())) {
- userGroupPermissions.add(group.getId());
- }
- for (Device device : groupTree.getDevices(groupPermission.getPropertyId())) {
- userDevicePermissions.add(device.getId());
- }
- }
-
- for (Permission devicePermission : dataManager.getPermissions(User.class, Device.class)) {
- getDevicePermissions(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
- }
-
- groupDevices.clear();
- for (long groupId : Context.getGroupsManager().getAllItems()) {
- for (Device device : groupTree.getDevices(groupId)) {
- getGroupDevices(groupId).add(device.getId());
- }
- }
-
- } catch (StorageException | ClassNotFoundException error) {
- LOGGER.warn("Refresh device permissions error", error);
- }
-
- deviceUsers.clear();
- for (Map.Entry<Long, Set<Long>> entry : devicePermissions.entrySet()) {
- for (long deviceId : entry.getValue()) {
- getAllDeviceUsers(deviceId).add(entry.getKey());
- }
- }
- } finally {
- writeUnlock();
- }
- }
-
- public boolean getUserAdmin(long userId) {
- User user = getUser(userId);
- return user != null && user.getAdministrator();
- }
-
- public void checkAdmin(long userId) throws SecurityException {
- if (!getUserAdmin(userId)) {
- throw new SecurityException("Admin access required");
- }
- }
-
- public boolean getUserManager(long userId) {
- User user = getUser(userId);
- return user != null && user.getUserLimit() != 0;
- }
-
- public void checkManager(long userId) throws SecurityException {
- if (!getUserManager(userId)) {
- throw new SecurityException("Manager access required");
- }
- }
-
- public void checkManager(long userId, long managedUserId) throws SecurityException {
- checkManager(userId);
- if (!usersManager.getUserItems(userId).contains(managedUserId)) {
- throw new SecurityException("User access denied");
- }
- }
-
- public void checkUserLimit(long userId) throws SecurityException {
- int userLimit = getUser(userId).getUserLimit();
- if (userLimit != -1 && usersManager.getUserItems(userId).size() >= userLimit) {
- throw new SecurityException("Manager user limit reached");
- }
- }
-
- public void checkDeviceLimit(long userId) throws SecurityException {
- int deviceLimit = getUser(userId).getDeviceLimit();
- if (deviceLimit != -1) {
- int deviceCount;
- if (getUserManager(userId)) {
- deviceCount = Context.getDeviceManager().getAllManagedItems(userId).size();
- } else {
- deviceCount = Context.getDeviceManager().getAllUserItems(userId).size();
- }
- if (deviceCount >= deviceLimit) {
- throw new SecurityException("User device limit reached");
- }
- }
- }
-
- public boolean getUserReadonly(long userId) {
- User user = getUser(userId);
- return user != null && user.getReadonly();
- }
-
- public boolean getUserDeviceReadonly(long userId) {
- User user = getUser(userId);
- return user != null && user.getDeviceReadonly();
- }
-
- public boolean getUserLimitCommands(long userId) {
- User user = getUser(userId);
- return user != null && user.getLimitCommands();
- }
-
- public boolean getUserDisableReport(long userId) {
- User user = getUser(userId);
- return user != null && user.getDisableReports();
- }
-
- public void checkReadonly(long userId) throws SecurityException {
- if (!getUserAdmin(userId) && (server.getReadonly() || getUserReadonly(userId))) {
- throw new SecurityException("Account is readonly");
- }
- }
-
- public void checkDeviceReadonly(long userId) throws SecurityException {
- if (!getUserAdmin(userId) && (server.getDeviceReadonly() || getUserDeviceReadonly(userId))) {
- throw new SecurityException("Account is device readonly");
- }
- }
-
- public void checkLimitCommands(long userId) throws SecurityException {
- if (!getUserAdmin(userId) && (server.getLimitCommands() || getUserLimitCommands(userId))) {
- throw new SecurityException("Account has limit sending commands");
- }
- }
-
- public void checkDisableReports(long userId) throws SecurityException {
- if (!getUserAdmin(userId) && (server.getDisableReports() || getUserDisableReport(userId))) {
- throw new SecurityException("Account has reports disabled");
- }
- }
-
- public void checkUserDeviceCommand(long userId, long deviceId, long commandId) throws SecurityException {
- if (!getUserAdmin(userId) && Context.getCommandsManager().checkDeviceCommand(deviceId, commandId)) {
- throw new SecurityException("Command can not be sent to this device");
- }
- }
-
- public void checkUserEnabled(long userId) throws SecurityException {
- User user = getUser(userId);
- if (user == null) {
- throw new SecurityException("Unknown account");
- }
- if (user.getDisabled()) {
- throw new SecurityException("Account is disabled");
- }
- if (user.getExpirationTime() != null && System.currentTimeMillis() > user.getExpirationTime().getTime()) {
- throw new SecurityException("Account has expired");
- }
- }
-
- public void checkUserUpdate(long userId, User before, User after) throws SecurityException {
- if (before.getAdministrator() != after.getAdministrator()
- || before.getDeviceLimit() != after.getDeviceLimit()
- || before.getUserLimit() != after.getUserLimit()) {
- checkAdmin(userId);
- }
- User user = getUser(userId);
- if (user != null && user.getExpirationTime() != null
- && (after.getExpirationTime() == null
- || user.getExpirationTime().compareTo(after.getExpirationTime()) < 0)) {
- checkAdmin(userId);
- }
- if (before.getReadonly() != after.getReadonly()
- || before.getDeviceReadonly() != after.getDeviceReadonly()
- || before.getDisabled() != after.getDisabled()
- || before.getLimitCommands() != after.getLimitCommands()
- || before.getDisableReports() != after.getDisableReports()) {
- if (userId == after.getId()) {
- checkAdmin(userId);
- }
- if (!getUserAdmin(userId)) {
- checkManager(userId);
- }
- }
- }
-
- public void checkUser(long userId, long managedUserId) throws SecurityException {
- if (userId != managedUserId && !getUserAdmin(userId)) {
- checkManager(userId, managedUserId);
- }
- }
-
- public void checkGroup(long userId, long groupId) throws SecurityException {
- if (!getGroupPermissions(userId).contains(groupId) && !getUserAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : usersManager.getUserItems(userId)) {
- if (getGroupPermissions(managedUserId).contains(groupId)) {
- return;
- }
- }
- throw new SecurityException("Group access denied");
- }
- }
-
- public void checkDevice(long userId, long deviceId) throws SecurityException {
- if (!Context.getDeviceManager().getUserItems(userId).contains(deviceId) && !getUserAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : usersManager.getUserItems(userId)) {
- if (Context.getDeviceManager().getUserItems(managedUserId).contains(deviceId)) {
- return;
- }
- }
- throw new SecurityException("Device access denied");
- }
- }
-
- public void checkRegistration(long userId) {
- if (!server.getRegistration() && !getUserAdmin(userId)) {
- throw new SecurityException("Registration disabled");
- }
- }
-
- public void checkPermission(Class<?> object, long userId, long objectId)
- throws SecurityException {
- SimpleObjectManager<? extends BaseModel> manager = null;
-
- if (object.equals(Device.class)) {
- checkDevice(userId, objectId);
- } else if (object.equals(Group.class)) {
- checkGroup(userId, objectId);
- } else if (object.equals(User.class) || object.equals(ManagedUser.class)) {
- checkUser(userId, objectId);
- } else if (object.equals(Geofence.class)) {
- manager = Context.getGeofenceManager();
- } else if (object.equals(Attribute.class)) {
- manager = Context.getAttributesManager();
- } else if (object.equals(Driver.class)) {
- manager = Context.getDriversManager();
- } else if (object.equals(Calendar.class)) {
- manager = Context.getCalendarManager();
- } else if (object.equals(Command.class)) {
- manager = Context.getCommandsManager();
- } else if (object.equals(Maintenance.class)) {
- manager = Context.getMaintenancesManager();
- } else if (object.equals(Notification.class)) {
- manager = Context.getNotificationManager();
- } else if (object.equals(Order.class)) {
- manager = Context.getOrderManager();
- } else {
- throw new IllegalArgumentException("Unknown object type");
- }
-
- if (manager != null && !manager.checkItemPermission(userId, objectId) && !getUserAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : usersManager.getManagedItems(userId)) {
- if (manager.checkItemPermission(managedUserId, objectId)) {
- return;
- }
- }
- throw new SecurityException("Type " + object + " access denied");
- }
- }
-
- public void refreshAllUsersPermissions() {
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshUserItems();
- }
- Context.getCalendarManager().refreshUserItems();
- Context.getDriversManager().refreshUserItems();
- Context.getAttributesManager().refreshUserItems();
- Context.getCommandsManager().refreshUserItems();
- Context.getMaintenancesManager().refreshUserItems();
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().refreshUserItems();
- }
- }
-
- public void refreshAllExtendedPermissions() {
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshExtendedPermissions();
- }
- Context.getDriversManager().refreshExtendedPermissions();
- Context.getAttributesManager().refreshExtendedPermissions();
- Context.getCommandsManager().refreshExtendedPermissions();
- Context.getMaintenancesManager().refreshExtendedPermissions();
- }
-
- public void refreshPermissions(Permission permission) {
- if (permission.getOwnerClass().equals(User.class)) {
- if (permission.getPropertyClass().equals(Device.class)
- || permission.getPropertyClass().equals(Group.class)) {
- refreshDeviceAndGroupPermissions();
- refreshAllExtendedPermissions();
- } else if (permission.getPropertyClass().equals(ManagedUser.class)) {
- usersManager.refreshUserItems();
- } else if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Driver.class)) {
- Context.getDriversManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Attribute.class)) {
- Context.getAttributesManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Calendar.class)) {
- Context.getCalendarManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Command.class)) {
- Context.getCommandsManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Maintenance.class)) {
- Context.getMaintenancesManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Order.class)) {
- Context.getOrderManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Notification.class)
- && Context.getNotificationManager() != null) {
- Context.getNotificationManager().refreshUserItems();
- }
- } else if (permission.getOwnerClass().equals(Device.class) || permission.getOwnerClass().equals(Group.class)) {
- if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Driver.class)) {
- Context.getDriversManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Attribute.class)) {
- Context.getAttributesManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Command.class)) {
- Context.getCommandsManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Maintenance.class)) {
- Context.getMaintenancesManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Order.class)) {
- Context.getOrderManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Notification.class)
- && Context.getNotificationManager() != null) {
- Context.getNotificationManager().refreshExtendedPermissions();
- }
- }
- }
-
- public Server getServer() {
- return server;
- }
-
- public void updateServer(Server server) throws StorageException {
- dataManager.updateObject(server);
- this.server = server;
- }
-
- public User login(String email, String password) throws StorageException {
- User user = dataManager.login(email, password);
- if (user != null) {
- checkUserEnabled(user.getId());
- return getUser(user.getId());
- }
- return null;
- }
-
- public Object lookupAttribute(long userId, String key, Object defaultValue) {
- Object preference;
- Object serverPreference = server.getAttributes().get(key);
- Object userPreference = getUser(userId).getAttributes().get(key);
- if (server.getForceSettings()) {
- preference = serverPreference != null ? serverPreference : userPreference;
- } else {
- preference = userPreference != null ? userPreference : serverPreference;
- }
- return preference != null ? preference : defaultValue;
- }
-
-}
diff --git a/src/main/java/org/traccar/database/SimpleObjectManager.java b/src/main/java/org/traccar/database/SimpleObjectManager.java
deleted file mode 100644
index 78701720f..000000000
--- a/src/main/java/org/traccar/database/SimpleObjectManager.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Permission;
-import org.traccar.model.User;
-import org.traccar.storage.StorageException;
-
-public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjectManager<T>
- implements ManagableObjects {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(SimpleObjectManager.class);
-
- private Map<Long, Set<Long>> userItems;
-
- protected SimpleObjectManager(DataManager dataManager, Class<T> baseClass) {
- super(dataManager, baseClass);
- }
-
- @Override
- public final Set<Long> getUserItems(long userId) {
- try {
- readLock();
- Set<Long> result = userItems.get(userId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = getUserItems(userId);
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getUserItems(managedUserId));
- }
- return result;
- }
-
- public final boolean checkItemPermission(long userId, long itemId) {
- return getUserItems(userId).contains(itemId);
- }
-
- @Override
- public void refreshItems() {
- super.refreshItems();
- refreshUserItems();
- }
-
- public final void refreshUserItems() {
- if (getDataManager() != null) {
- try {
- writeLock();
- userItems = new ConcurrentHashMap<>();
- for (Permission permission : getDataManager().getPermissions(User.class, getBaseClass())) {
- Set<Long> items = userItems.computeIfAbsent(permission.getOwnerId(), key -> new HashSet<>());
- items.add(permission.getPropertyId());
- }
- } catch (StorageException | ClassNotFoundException error) {
- LOGGER.warn("Error getting permissions", error);
- } finally {
- writeUnlock();
- }
- }
- }
-
- @Override
- public void removeItem(long itemId) throws StorageException {
- super.removeItem(itemId);
- refreshUserItems();
- }
-
-}
diff --git a/src/main/java/org/traccar/database/StatisticsManager.java b/src/main/java/org/traccar/database/StatisticsManager.java
index 3579ce7a5..445e53e7c 100644
--- a/src/main/java/org/traccar/database/StatisticsManager.java
+++ b/src/main/java/org/traccar/database/StatisticsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 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.
@@ -19,16 +19,21 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.api.security.ServiceAccountUser;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.DateUtil;
import org.traccar.model.Statistics;
+import org.traccar.storage.Storage;
import org.traccar.storage.StorageException;
-
-import javax.inject.Inject;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.Form;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Request;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Form;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
@@ -37,6 +42,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+@Singleton
public class StatisticsManager {
private static final Logger LOGGER = LoggerFactory.getLogger(StatisticsManager.class);
@@ -44,7 +50,7 @@ public class StatisticsManager {
private static final int SPLIT_MODE = Calendar.DAY_OF_MONTH;
private final Config config;
- private final DataManager dataManager;
+ private final Storage storage;
private final Client client;
private final ObjectMapper objectMapper;
@@ -52,6 +58,7 @@ public class StatisticsManager {
private final Set<Long> users = new HashSet<>();
private final Map<Long, String> deviceProtocols = new HashMap<>();
+ private final Map<Long, Integer> deviceMessages = new HashMap<>();
private int requests;
private int messagesReceived;
@@ -62,9 +69,9 @@ public class StatisticsManager {
private int geolocationRequests;
@Inject
- public StatisticsManager(Config config, DataManager dataManager, Client client, ObjectMapper objectMapper) {
+ public StatisticsManager(Config config, Storage storage, Client client, ObjectMapper objectMapper) {
this.config = config;
- this.dataManager = dataManager;
+ this.storage = storage;
this.client = client;
this.objectMapper = objectMapper;
}
@@ -93,8 +100,11 @@ public class StatisticsManager {
statistics.setProtocols(protocols);
}
+ statistics.set("modern", config.getString(Keys.WEB_PATH).contains("modern"));
+
users.clear();
deviceProtocols.clear();
+ deviceMessages.clear();
requests = 0;
messagesReceived = 0;
messagesStored = 0;
@@ -105,7 +115,7 @@ public class StatisticsManager {
}
try {
- dataManager.addObject(statistics);
+ storage.addObject(statistics, new Request(new Columns.Exclude("id")));
} catch (StorageException e) {
LOGGER.warn("Error saving statistics", e);
}
@@ -133,6 +143,13 @@ public class StatisticsManager {
LOGGER.warn("Failed to serialize protocols", e);
}
}
+ if (!statistics.getAttributes().isEmpty()) {
+ try {
+ form.param("attributes", objectMapper.writeValueAsString(statistics.getAttributes()));
+ } catch (JsonProcessingException e) {
+ LOGGER.warn("Failed to serialize attributes", e);
+ }
+ }
client.target(url).request().async().post(Entity.form(form));
}
@@ -142,7 +159,7 @@ public class StatisticsManager {
public synchronized void registerRequest(long userId) {
checkSplit();
requests += 1;
- if (userId != 0) {
+ if (userId != 0 && userId != ServiceAccountUser.ID) {
users.add(userId);
}
}
@@ -157,9 +174,14 @@ public class StatisticsManager {
messagesStored += 1;
if (deviceId != 0) {
deviceProtocols.put(deviceId, protocol);
+ deviceMessages.merge(deviceId, 1, Integer::sum);
}
}
+ public synchronized int messageStoredCount(long deviceId) {
+ return deviceMessages.getOrDefault(deviceId, 0);
+ }
+
public synchronized void registerMail() {
checkSplit();
mailSent += 1;
diff --git a/src/main/java/org/traccar/database/UsersManager.java b/src/main/java/org/traccar/database/UsersManager.java
deleted file mode 100644
index 31759dc8b..000000000
--- a/src/main/java/org/traccar/database/UsersManager.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.traccar.model.User;
-import org.traccar.storage.StorageException;
-
-public class UsersManager extends SimpleObjectManager<User> {
-
- private Map<String, User> usersTokens;
-
- public UsersManager(DataManager dataManager) {
- super(dataManager, User.class);
- if (usersTokens == null) {
- usersTokens = new ConcurrentHashMap<>();
- }
- }
-
- private void putToken(User user) {
- if (usersTokens == null) {
- usersTokens = new ConcurrentHashMap<>();
- }
- if (user.getToken() != null) {
- usersTokens.put(user.getToken(), user);
- }
- }
-
- @Override
- protected void addNewItem(User user) {
- super.addNewItem(user);
- putToken(user);
- }
-
- @Override
- protected void updateCachedItem(User user) {
- User cachedUser = getById(user.getId());
- super.updateCachedItem(user);
- putToken(user);
- if (cachedUser.getToken() != null && !cachedUser.getToken().equals(user.getToken())) {
- usersTokens.remove(cachedUser.getToken());
- }
- }
-
- @Override
- public void updateItem(User user) throws StorageException {
- if (user.getHashedPassword() != null) {
- getDataManager().updateUserPassword(user);
- }
- super.updateItem(user);
- }
-
- @Override
- protected void removeCachedItem(long userId) {
- User cachedUser = getById(userId);
- if (cachedUser != null) {
- String userToken = cachedUser.getToken();
- super.removeCachedItem(userId);
- if (userToken != null) {
- usersTokens.remove(userToken);
- }
- }
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = getUserItems(userId);
- result.add(userId);
- return result;
- }
-
- public User getUserByToken(String token) {
- return usersTokens.get(token);
- }
-
-}