diff options
author | casswarry0 <casswarry0@gmail.com> | 2023-01-17 17:14:53 -0700 |
---|---|---|
committer | casswarry0 <casswarry0@gmail.com> | 2023-01-17 17:14:53 -0700 |
commit | 7338b8730949ed027b3f8b31d7dca20687ebbb8b (patch) | |
tree | c2d171e6121818ab511460a786f69aab97a2a628 /src/main/java/org/traccar/database | |
parent | cdecd3fa4427a382c0b09f8ad9d69ec14388960a (diff) | |
parent | 85501f9cf4918d5eee345f83aed7a31eecb26b8d (diff) | |
download | trackermap-server-7338b8730949ed027b3f8b31d7dca20687ebbb8b.tar.gz trackermap-server-7338b8730949ed027b3f8b31d7dca20687ebbb8b.tar.bz2 trackermap-server-7338b8730949ed027b3f8b31d7dca20687ebbb8b.zip |
Merge branch 'master' into develop
Diffstat (limited to 'src/main/java/org/traccar/database')
28 files changed, 353 insertions, 3609 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 be6310033..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.sql.SQLException; -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; - -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 (SQLException 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 SQLException { - 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 SQLException { - dataManager.updateObject(item); - updateCachedItem(item); - } - - protected void removeCachedItem(long itemId) { - try { - writeLock(); - items.remove(itemId); - } finally { - writeUnlock(); - } - } - - public void removeItem(long itemId) throws SQLException { - 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..df399cd7a 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 javax.annotation.Nullable; +import javax.inject.Inject; +import javax.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 e12d44612..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 java.net.SocketAddress; -import java.sql.SQLException; -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 (SQLException error) { - LOGGER.warn("Update device status error", error); - } - - 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 00c802fde..000000000 --- a/src/main/java/org/traccar/database/DataManager.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright 2012 - 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 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.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.config.Config; -import org.traccar.config.Keys; -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.Event; -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.Position; -import org.traccar.model.Server; -import org.traccar.model.Statistics; -import org.traccar.model.User; - -import javax.sql.DataSource; -import java.beans.Introspector; -import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.sql.SQLException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -public class DataManager { - - private static final Logger LOGGER = LoggerFactory.getLogger(DataManager.class); - - public static final String ACTION_SELECT_ALL = "selectAll"; - public static final String ACTION_SELECT = "select"; - public static final String ACTION_INSERT = "insert"; - public static final String ACTION_UPDATE = "update"; - public static final String ACTION_DELETE = "delete"; - - private final Config config; - - private DataSource dataSource; - - public DataSource getDataSource() { - return dataSource; - } - - private boolean generateQueries; - - private final boolean forceLdap; - - public DataManager(Config config) throws Exception { - this.config = config; - - forceLdap = config.getBoolean(Keys.LDAP_FORCE); - - initDatabase(); - initDatabaseSchema(); - } - - 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); - } - - generateQueries = config.getBoolean(Keys.DATABASE_GENERATE_QUERIES); - - dataSource = new HikariDataSource(hikariConfig); - } - - public static String constructObjectQuery(String action, Class<?> clazz, boolean extended) { - switch (action) { - case ACTION_INSERT: - case ACTION_UPDATE: - StringBuilder result = new StringBuilder(); - StringBuilder fields = new StringBuilder(); - StringBuilder values = new StringBuilder(); - - Set<Method> methods = new HashSet<>(Arrays.asList(clazz.getMethods())); - methods.removeAll(Arrays.asList(Object.class.getMethods())); - methods.removeAll(Arrays.asList(BaseModel.class.getMethods())); - for (Method method : methods) { - boolean skip; - if (extended) { - skip = !method.isAnnotationPresent(QueryExtended.class); - } else { - skip = method.isAnnotationPresent(QueryIgnore.class) - || method.isAnnotationPresent(QueryExtended.class) && !action.equals(ACTION_INSERT); - } - if (!skip && method.getName().startsWith("get") && method.getParameterTypes().length == 0) { - String name = Introspector.decapitalize(method.getName().substring(3)); - if (action.equals(ACTION_INSERT)) { - fields.append(name).append(", "); - values.append(":").append(name).append(", "); - } else { - fields.append(name).append(" = :").append(name).append(", "); - } - } - } - fields.setLength(fields.length() - 2); - if (action.equals(ACTION_INSERT)) { - values.setLength(values.length() - 2); - result.append("INSERT INTO ").append(getObjectsTableName(clazz)).append(" ("); - result.append(fields).append(") "); - result.append("VALUES (").append(values).append(")"); - } else { - result.append("UPDATE ").append(getObjectsTableName(clazz)).append(" SET "); - result.append(fields); - result.append(" WHERE id = :id"); - } - return result.toString(); - case ACTION_SELECT_ALL: - return "SELECT * FROM " + getObjectsTableName(clazz); - case ACTION_SELECT: - return "SELECT * FROM " + getObjectsTableName(clazz) + " WHERE id = :id"; - case ACTION_DELETE: - return "DELETE FROM " + getObjectsTableName(clazz) + " WHERE id = :id"; - default: - throw new IllegalArgumentException("Unknown action"); - } - } - - public static String constructPermissionQuery(String action, Class<?> owner, Class<?> property) { - switch (action) { - case ACTION_SELECT_ALL: - return "SELECT " + makeNameId(owner) + ", " + makeNameId(property) + " FROM " - + getPermissionsTableName(owner, property); - case ACTION_INSERT: - return "INSERT INTO " + getPermissionsTableName(owner, property) - + " (" + makeNameId(owner) + ", " + makeNameId(property) + ") VALUES (:" - + makeNameId(owner) + ", :" + makeNameId(property) + ")"; - case ACTION_DELETE: - return "DELETE FROM " + getPermissionsTableName(owner, property) - + " WHERE " + makeNameId(owner) + " = :" + makeNameId(owner) - + " AND " + makeNameId(property) + " = :" + makeNameId(property); - default: - throw new IllegalArgumentException("Unknown action"); - } - } - - private String getQuery(String key) { - String query = config.getString(key); - if (query == null) { - LOGGER.info("Query not provided: " + key); - } - return query; - } - - public String getQuery(String action, Class<?> clazz) { - return getQuery(action, clazz, false); - } - - public String getQuery(String action, Class<?> clazz, boolean extended) { - String queryName; - if (action.equals(ACTION_SELECT_ALL)) { - queryName = "database.select" + clazz.getSimpleName() + "s"; - } else { - queryName = "database." + action.toLowerCase() + clazz.getSimpleName(); - if (extended) { - queryName += "Extended"; - } - } - String query = config.getString(queryName); - if (query == null) { - if (generateQueries) { - query = constructObjectQuery(action, clazz, extended); - } else { - LOGGER.info("Query not provided: " + queryName); - } - } - return query; - } - - public String getQuery(String action, Class<?> owner, Class<?> property) { - String queryName; - switch (action) { - case ACTION_SELECT_ALL: - queryName = "database.select" + owner.getSimpleName() + property.getSimpleName() + "s"; - break; - case ACTION_INSERT: - queryName = "database.link" + owner.getSimpleName() + property.getSimpleName(); - break; - default: - queryName = "database.unlink" + owner.getSimpleName() + property.getSimpleName(); - break; - } - String query = config.getString(queryName); - if (query == null) { - if (generateQueries) { - query = constructPermissionQuery( - action, owner, property.equals(User.class) ? ManagedUser.class : property); - } else { - LOGGER.info("Query not provided: " + queryName); - } - } - return query; - } - - private static String getPermissionsTableName(Class<?> owner, Class<?> property) { - String propertyName = property.getSimpleName(); - if (propertyName.equals("ManagedUser")) { - propertyName = "User"; - } - return "tc_" + Introspector.decapitalize(owner.getSimpleName()) - + "_" + Introspector.decapitalize(propertyName); - } - - private static String getObjectsTableName(Class<?> clazz) { - String result = "tc_" + Introspector.decapitalize(clazz.getSimpleName()); - // Add "s" ending if object name is not plural already - if (!result.endsWith("s")) { - result += "s"; - } - return result; - } - - 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 SQLException { - User user = QueryBuilder.create(dataSource, getQuery("database.loginUser")) - .setString("email", email.trim()) - .executeQuerySingle(User.class); - 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 updateDeviceStatus(Device device) throws SQLException { - QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, Device.class, true)) - .setObject(device) - .executeUpdate(); - } - - public Collection<Position> getPositions(long deviceId, Date from, Date to) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectPositions")) - .setLong("deviceId", deviceId) - .setDate("from", from) - .setDate("to", to) - .executeQuery(Position.class); - } - - public void updateLatestPosition(Position position) throws SQLException { - QueryBuilder.create(dataSource, getQuery("database.updateLatestPosition")) - .setDate("now", new Date()) - .setObject(position) - .executeUpdate(); - } - - public Collection<Position> getLatestPositions() throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectLatestPositions")) - .executeQuery(Position.class); - } - - public Server getServer() throws SQLException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, Server.class)) - .executeQuerySingle(Server.class); - } - - public Collection<Event> getEvents(long deviceId, Date from, Date to) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectEvents")) - .setLong("deviceId", deviceId) - .setDate("from", from) - .setDate("to", to) - .executeQuery(Event.class); - } - - public Collection<Statistics> getStatistics(Date from, Date to) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectStatistics")) - .setDate("from", from) - .setDate("to", to) - .executeQuery(Statistics.class); - } - - public static Class<?> getClassByName(String name) throws ClassNotFoundException { - switch (name.toLowerCase().replace("id", "")) { - case "device": - return Device.class; - case "group": - return Group.class; - case "user": - return User.class; - case "manageduser": - return ManagedUser.class; - case "geofence": - return Geofence.class; - case "driver": - return Driver.class; - case "attribute": - return Attribute.class; - case "calendar": - return Calendar.class; - case "command": - return Command.class; - case "maintenance": - return Maintenance.class; - case "notification": - return Notification.class; - case "order": - return Order.class; - default: - throw new ClassNotFoundException(); - } - } - - private static String makeNameId(Class<?> clazz) { - String name = clazz.getSimpleName(); - return Introspector.decapitalize(name) + (!name.contains("Id") ? "Id" : ""); - } - - public Collection<Permission> getPermissions(Class<? extends BaseModel> owner, Class<? extends BaseModel> property) - throws SQLException, ClassNotFoundException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, owner, property)) - .executePermissionsQuery(); - } - - public void linkObject(Class<?> owner, long ownerId, Class<?> property, long propertyId, boolean link) - throws SQLException { - QueryBuilder.create(dataSource, getQuery(link ? ACTION_INSERT : ACTION_DELETE, owner, property)) - .setLong(makeNameId(owner), ownerId) - .setLong(makeNameId(property), propertyId) - .executeUpdate(); - } - - public <T extends BaseModel> T getObject(Class<T> clazz, long entityId) throws SQLException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT, clazz)) - .setLong("id", entityId) - .executeQuerySingle(clazz); - } - - public <T extends BaseModel> Collection<T> getObjects(Class<T> clazz) throws SQLException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, clazz)) - .executeQuery(clazz); - } - - public void addObject(BaseModel entity) throws SQLException { - entity.setId(QueryBuilder.create(dataSource, getQuery(ACTION_INSERT, entity.getClass()), true) - .setObject(entity) - .executeUpdate()); - } - - public void updateObject(BaseModel entity) throws SQLException { - QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, entity.getClass())) - .setObject(entity) - .executeUpdate(); - if (entity instanceof User && ((User) entity).getHashedPassword() != null) { - QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, User.class, true)) - .setObject(entity) - .executeUpdate(); - } - } - - public void removeObject(Class<? extends BaseModel> clazz, long entityId) throws SQLException { - QueryBuilder.create(dataSource, getQuery(ACTION_DELETE, clazz)) - .setLong("id", entityId) - .executeUpdate(); - } - -} 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..583b2ae35 --- /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 javax.inject.Inject; +import javax.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 c8a99274c..000000000 --- a/src/main/java/org/traccar/database/DeviceManager.java +++ /dev/null @@ -1,483 +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; - -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 (SQLException e) { - LOGGER.warn("Automatic device registration error", e); - return 0; - } - } - - public void updateDeviceCache(boolean force) throws SQLException { - 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; - } - - public Device getDeviceByPhone(String phone) { - try { - readLock(); - return devicesByPhone.get(phone); - } finally { - readUnlock(); - } - } - - @Override - public Set<Long> getAllItems() { - Set<Long> result = super.getAllItems(); - if (result.isEmpty()) { - try { - updateDeviceCache(true); - } catch (SQLException e) { - LOGGER.warn("Update device cache error", e); - } - 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 SQLException { - 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 (SQLException 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 SQLException { - - 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 SQLException { - 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 93e5820fb..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.sql.SQLException; -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; - -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 SQLException { - 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 (SQLException | 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 c35f35f93..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.sql.SQLException; -import java.util.HashSet; -import java.util.Set; - -import org.traccar.Context; -import org.traccar.model.Group; - -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 SQLException { - 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..c1ef810ee 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 javax.inject.Inject; +import javax.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 9f9a83cd2..cb971b082 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.sql.SQLException; -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 javax.annotation.Nullable; +import javax.inject.Inject; +import javax.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); - } catch (SQLException error) { + 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(user, event, position); + } catch (MessageException | InterruptedException 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/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 32464cf90..000000000 --- a/src/main/java/org/traccar/database/PermissionsManager.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 2015 - 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.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 java.sql.SQLException; -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 (SQLException 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 (SQLException | 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 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 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()) { - 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 SQLException { - dataManager.updateObject(server); - this.server = server; - } - - public User login(String email, String password) throws SQLException { - 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/QueryBuilder.java b/src/main/java/org/traccar/database/QueryBuilder.java deleted file mode 100644 index 396cbf562..000000000 --- a/src/main/java/org/traccar/database/QueryBuilder.java +++ /dev/null @@ -1,485 +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 com.fasterxml.jackson.core.JsonProcessingException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.model.Permission; - -import javax.sql.DataSource; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Timestamp; -import java.sql.Types; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public final class QueryBuilder { - - private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class); - - private final Map<String, List<Integer>> indexMap = new HashMap<>(); - private Connection connection; - private PreparedStatement statement; - private final String query; - private final boolean returnGeneratedKeys; - - private QueryBuilder(DataSource dataSource, String query, boolean returnGeneratedKeys) throws SQLException { - this.query = query; - this.returnGeneratedKeys = returnGeneratedKeys; - if (query != null) { - connection = dataSource.getConnection(); - String parsedQuery = parse(query.trim(), indexMap); - try { - if (returnGeneratedKeys) { - statement = connection.prepareStatement(parsedQuery, Statement.RETURN_GENERATED_KEYS); - } else { - statement = connection.prepareStatement(parsedQuery); - } - } catch (SQLException error) { - connection.close(); - throw error; - } - } - } - - private static String parse(String query, Map<String, List<Integer>> paramMap) { - - int length = query.length(); - StringBuilder parsedQuery = new StringBuilder(length); - boolean inSingleQuote = false; - boolean inDoubleQuote = false; - int index = 1; - - for (int i = 0; i < length; i++) { - - char c = query.charAt(i); - - // String end - if (inSingleQuote) { - if (c == '\'') { - inSingleQuote = false; - } - } else if (inDoubleQuote) { - if (c == '"') { - inDoubleQuote = false; - } - } else { - - // String begin - if (c == '\'') { - inSingleQuote = true; - } else if (c == '"') { - inDoubleQuote = true; - } else if (c == ':' && i + 1 < length - && Character.isJavaIdentifierStart(query.charAt(i + 1))) { - - // Identifier name - int j = i + 2; - while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) { - j++; - } - - String name = query.substring(i + 1, j); - c = '?'; - i += name.length(); - name = name.toLowerCase(); - - // Add to list - List<Integer> indexList = paramMap.computeIfAbsent(name, k -> new LinkedList<>()); - indexList.add(index); - - index++; - } - } - - parsedQuery.append(c); - } - - return parsedQuery.toString(); - } - - public static QueryBuilder create(DataSource dataSource, String query) throws SQLException { - return new QueryBuilder(dataSource, query, false); - } - - public static QueryBuilder create( - DataSource dataSource, String query, boolean returnGeneratedKeys) throws SQLException { - return new QueryBuilder(dataSource, query, returnGeneratedKeys); - } - - private List<Integer> indexes(String name) { - name = name.toLowerCase(); - List<Integer> result = indexMap.get(name); - if (result == null) { - result = new LinkedList<>(); - } - return result; - } - - public QueryBuilder setBoolean(String name, boolean value) throws SQLException { - for (int i : indexes(name)) { - try { - statement.setBoolean(i, value); - } catch (SQLException error) { - statement.close(); - connection.close(); - throw error; - } - } - return this; - } - - public QueryBuilder setInteger(String name, int value) throws SQLException { - for (int i : indexes(name)) { - try { - statement.setInt(i, value); - } catch (SQLException error) { - statement.close(); - connection.close(); - throw error; - } - } - return this; - } - - public QueryBuilder setLong(String name, long value) throws SQLException { - return setLong(name, value, false); - } - - public QueryBuilder setLong(String name, long value, boolean nullIfZero) throws SQLException { - for (int i : indexes(name)) { - try { - if (value == 0 && nullIfZero) { - statement.setNull(i, Types.INTEGER); - } else { - statement.setLong(i, value); - } - } catch (SQLException error) { - statement.close(); - connection.close(); - throw error; - } - } - return this; - } - - public QueryBuilder setDouble(String name, double value) throws SQLException { - for (int i : indexes(name)) { - try { - statement.setDouble(i, value); - } catch (SQLException error) { - statement.close(); - connection.close(); - throw error; - } - } - return this; - } - - public QueryBuilder setString(String name, String value) throws SQLException { - for (int i : indexes(name)) { - try { - if (value == null) { - statement.setNull(i, Types.VARCHAR); - } else { - statement.setString(i, value); - } - } catch (SQLException error) { - statement.close(); - connection.close(); - throw error; - } - } - return this; - } - - public QueryBuilder setDate(String name, Date value) throws SQLException { - for (int i : indexes(name)) { - try { - if (value == null) { - statement.setNull(i, Types.TIMESTAMP); - } else { - statement.setTimestamp(i, new Timestamp(value.getTime())); - } - } catch (SQLException error) { - statement.close(); - connection.close(); - throw error; - } - } - return this; - } - - public QueryBuilder setBlob(String name, byte[] value) throws SQLException { - for (int i : indexes(name)) { - try { - if (value == null) { - statement.setNull(i, Types.BLOB); - } else { - statement.setBytes(i, value); - } - } catch (SQLException error) { - statement.close(); - connection.close(); - throw error; - } - } - return this; - } - - public QueryBuilder setObject(Object object) throws SQLException { - - Method[] methods = object.getClass().getMethods(); - - for (Method method : methods) { - if (method.getName().startsWith("get") && method.getParameterTypes().length == 0 - && !method.isAnnotationPresent(QueryIgnore.class)) { - String name = method.getName().substring(3); - try { - if (method.getReturnType().equals(boolean.class)) { - setBoolean(name, (Boolean) method.invoke(object)); - } else if (method.getReturnType().equals(int.class)) { - setInteger(name, (Integer) method.invoke(object)); - } else if (method.getReturnType().equals(long.class)) { - setLong(name, (Long) method.invoke(object), name.endsWith("Id")); - } else if (method.getReturnType().equals(double.class)) { - setDouble(name, (Double) method.invoke(object)); - } else if (method.getReturnType().equals(String.class)) { - setString(name, (String) method.invoke(object)); - } else if (method.getReturnType().equals(Date.class)) { - setDate(name, (Date) method.invoke(object)); - } else if (method.getReturnType().equals(byte[].class)) { - setBlob(name, (byte[]) method.invoke(object)); - } else { - setString(name, Context.getObjectMapper().writeValueAsString(method.invoke(object))); - } - } catch (IllegalAccessException | InvocationTargetException | JsonProcessingException error) { - LOGGER.warn("Get property error", error); - } - } - } - - return this; - } - - private interface ResultSetProcessor<T> { - void process(T object, ResultSet resultSet) throws SQLException; - } - - public <T> T executeQuerySingle(Class<T> clazz) throws SQLException { - Collection<T> result = executeQuery(clazz); - if (!result.isEmpty()) { - return result.iterator().next(); - } else { - return null; - } - } - - private <T> void addProcessors( - List<ResultSetProcessor<T>> processors, - final Class<?> parameterType, final Method method, final String name) { - - if (parameterType.equals(boolean.class)) { - processors.add((object, resultSet) -> { - try { - method.invoke(object, resultSet.getBoolean(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } - }); - } else if (parameterType.equals(int.class)) { - processors.add((object, resultSet) -> { - try { - method.invoke(object, resultSet.getInt(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } - }); - } else if (parameterType.equals(long.class)) { - processors.add((object, resultSet) -> { - try { - method.invoke(object, resultSet.getLong(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } - }); - } else if (parameterType.equals(double.class)) { - processors.add((object, resultSet) -> { - try { - method.invoke(object, resultSet.getDouble(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } - }); - } else if (parameterType.equals(String.class)) { - processors.add((object, resultSet) -> { - try { - method.invoke(object, resultSet.getString(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } - }); - } else if (parameterType.equals(Date.class)) { - processors.add((object, resultSet) -> { - try { - Timestamp timestamp = resultSet.getTimestamp(name); - if (timestamp != null) { - method.invoke(object, new Date(timestamp.getTime())); - } - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } - }); - } else if (parameterType.equals(byte[].class)) { - processors.add((object, resultSet) -> { - try { - method.invoke(object, resultSet.getBytes(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } - }); - } else { - processors.add((object, resultSet) -> { - String value = resultSet.getString(name); - if (value != null && !value.isEmpty()) { - try { - method.invoke(object, Context.getObjectMapper().readValue(value, parameterType)); - } catch (InvocationTargetException | IllegalAccessException | IOException error) { - LOGGER.warn("Set property error", error); - } - } - }); - } - } - - public <T> Collection<T> executeQuery(Class<T> clazz) throws SQLException { - List<T> result = new LinkedList<>(); - - if (query != null) { - - try { - - try (ResultSet resultSet = statement.executeQuery()) { - - ResultSetMetaData resultMetaData = resultSet.getMetaData(); - - List<ResultSetProcessor<T>> processors = new LinkedList<>(); - - Method[] methods = clazz.getMethods(); - - for (final Method method : methods) { - if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 - && !method.isAnnotationPresent(QueryIgnore.class)) { - - final String name = method.getName().substring(3); - - // Check if column exists - boolean column = false; - for (int i = 1; i <= resultMetaData.getColumnCount(); i++) { - if (name.equalsIgnoreCase(resultMetaData.getColumnLabel(i))) { - column = true; - break; - } - } - if (!column) { - continue; - } - - addProcessors(processors, method.getParameterTypes()[0], method, name); - } - } - - while (resultSet.next()) { - try { - T object = clazz.getDeclaredConstructor().newInstance(); - for (ResultSetProcessor<T> processor : processors) { - processor.process(object, resultSet); - } - result.add(object); - } catch (ReflectiveOperationException e) { - throw new IllegalArgumentException(); - } - } - } - - } finally { - statement.close(); - connection.close(); - } - } - - return result; - } - - public long executeUpdate() throws SQLException { - - if (query != null) { - try { - statement.execute(); - if (returnGeneratedKeys) { - ResultSet resultSet = statement.getGeneratedKeys(); - if (resultSet.next()) { - return resultSet.getLong(1); - } - } - } finally { - statement.close(); - connection.close(); - } - } - return 0; - } - - public Collection<Permission> executePermissionsQuery() throws SQLException, ClassNotFoundException { - List<Permission> result = new LinkedList<>(); - if (query != null) { - try { - try (ResultSet resultSet = statement.executeQuery()) { - ResultSetMetaData resultMetaData = resultSet.getMetaData(); - while (resultSet.next()) { - LinkedHashMap<String, Long> map = new LinkedHashMap<>(); - for (int i = 1; i <= resultMetaData.getColumnCount(); i++) { - String label = resultMetaData.getColumnLabel(i); - map.put(label, resultSet.getLong(label)); - } - result.add(new Permission(map)); - } - } - } finally { - statement.close(); - connection.close(); - } - } - - return result; - } - -} diff --git a/src/main/java/org/traccar/database/QueryExtended.java b/src/main/java/org/traccar/database/QueryExtended.java deleted file mode 100644 index 07bc2c211..000000000 --- a/src/main/java/org/traccar/database/QueryExtended.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.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface QueryExtended { -} diff --git a/src/main/java/org/traccar/database/QueryIgnore.java b/src/main/java/org/traccar/database/QueryIgnore.java deleted file mode 100644 index ac835cf2f..000000000 --- a/src/main/java/org/traccar/database/QueryIgnore.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 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.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface QueryIgnore { -} 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 eb8284d4e..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.sql.SQLException; -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; - -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 (SQLException | ClassNotFoundException error) { - LOGGER.warn("Error getting permissions", error); - } finally { - writeUnlock(); - } - } - } - - @Override - public void removeItem(long itemId) throws SQLException { - 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 4ad6d9d5c..e0995dabc 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. @@ -23,12 +23,16 @@ 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 org.traccar.storage.query.Columns; +import org.traccar.storage.query.Request; import javax.inject.Inject; +import javax.inject.Singleton; import javax.ws.rs.client.Client; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Form; -import java.sql.SQLException; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -37,6 +41,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 +49,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; @@ -62,9 +67,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; } @@ -105,8 +110,8 @@ public class StatisticsManager { } try { - dataManager.addObject(statistics); - } catch (SQLException e) { + storage.addObject(statistics, new Request(new Columns.Exclude("id"))); + } catch (StorageException e) { LOGGER.warn("Error saving statistics", e); } 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 b741a85b6..000000000 --- a/src/main/java/org/traccar/database/UsersManager.java +++ /dev/null @@ -1,84 +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; - -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 - 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); - } - -} |