package org.traccar.database; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.traccar.Context; import org.traccar.helper.Log; import org.traccar.model.Device; import org.traccar.model.Geofence; import org.traccar.model.GroupGeofence; import org.traccar.model.Position; import org.traccar.model.DeviceGeofence; import org.traccar.model.GeofencePermission; public class GeofenceManager { private final DataManager dataManager; private final Map geofences = new HashMap<>(); private final Map> userGeofences = new HashMap<>(); private final Map> groupGeofences = new HashMap<>(); private final Map> deviceGeofencesWithGroups = new HashMap<>(); private final Map> deviceGeofences = new HashMap<>(); private final ReadWriteLock deviceGeofencesLock = new ReentrantReadWriteLock(); private final ReadWriteLock geofencesLock = new ReentrantReadWriteLock(); private final ReadWriteLock groupGeofencesLock = new ReentrantReadWriteLock(); public GeofenceManager(DataManager dataManager) { this.dataManager = dataManager; refresh(); } public Set getUserGeofencesIds(long userId) { if (!userGeofences.containsKey(userId)) { userGeofences.put(userId, new HashSet()); } return userGeofences.get(userId); } private Set getGroupGeofences(long groupId) { if (!groupGeofences.containsKey(groupId)) { groupGeofences.put(groupId, new HashSet()); } return groupGeofences.get(groupId); } public Set getAllDeviceGeofences(long deviceId) { deviceGeofencesLock.readLock().lock(); try { return getDeviceGeofences(deviceGeofencesWithGroups, deviceId); } finally { deviceGeofencesLock.readLock().unlock(); } } public Set getDeviceGeofences(long deviceId) { deviceGeofencesLock.readLock().lock(); try { return getDeviceGeofences(deviceGeofences, deviceId); } finally { deviceGeofencesLock.readLock().unlock(); } } private Set getDeviceGeofences(Map> deviceGeofences, long deviceId) { if (!deviceGeofences.containsKey(deviceId)) { deviceGeofences.put(deviceId, new HashSet()); } return deviceGeofences.get(deviceId); } public final void refresh() { if (dataManager != null) { try { geofencesLock.writeLock().lock(); groupGeofencesLock.writeLock().lock(); deviceGeofencesLock.writeLock().lock(); try { geofences.clear(); for (Geofence geofence : dataManager.getGeofences()) { geofences.put(geofence.getId(), geofence); } userGeofences.clear(); for (GeofencePermission geofencePermission : dataManager.getGeofencePermissions()) { getUserGeofencesIds(geofencePermission.getUserId()).add(geofencePermission.getGeofenceId()); } groupGeofences.clear(); for (GroupGeofence groupGeofence : dataManager.getGroupGeofences()) { getGroupGeofences(groupGeofence.getGroupId()).add(groupGeofence.getGeofenceId()); } deviceGeofences.clear(); deviceGeofencesWithGroups.clear(); for (DeviceGeofence deviceGeofence : dataManager.getDeviceGeofences()) { getDeviceGeofences(deviceGeofences, deviceGeofence.getDeviceId()) .add(deviceGeofence.getGeofenceId()); getDeviceGeofences(deviceGeofencesWithGroups, deviceGeofence.getDeviceId()) .add(deviceGeofence.getGeofenceId()); } for (Device device : dataManager.getAllDevicesCached()) { long groupId = device.getGroupId(); while (groupId != 0) { getDeviceGeofences(deviceGeofencesWithGroups, device.getId()).addAll(getGroupGeofences(groupId)); groupId = dataManager.getGroupById(groupId).getGroupId(); } List deviceGeofenceIds = device.getGeofenceIds(); if (deviceGeofenceIds == null) { deviceGeofenceIds = new ArrayList(); } else { deviceGeofenceIds.clear(); } Position lastPosition = Context.getConnectionManager().getLastPosition(device.getId()); if (lastPosition != null) { for (Long geofenceId : deviceGeofencesWithGroups.get(device.getId())) { if (getGeofence(geofenceId).getGeometry() .containsPoint(lastPosition.getLatitude(), lastPosition.getLongitude())) { deviceGeofenceIds.add(geofenceId); } } } device.setGeofenceIds(deviceGeofenceIds); } } finally { geofencesLock.writeLock().unlock(); groupGeofencesLock.writeLock().unlock(); deviceGeofencesLock.writeLock().unlock(); } } catch (SQLException error) { Log.warning(error); } } } public final Collection getAllGeofences() { geofencesLock.readLock().lock(); try { return geofences.values(); } finally { geofencesLock.readLock().unlock(); } } public final Collection getUserGeofences(long userId) { geofencesLock.readLock().lock(); try { Collection result = new LinkedList<>(); for (Long geofenceId : getUserGeofencesIds(userId)) { result.add(getGeofence(geofenceId)); } return result; } finally { geofencesLock.readLock().unlock(); } } public final Geofence getGeofence(Long geofenceId) { geofencesLock.readLock().lock(); try { return geofences.get(geofenceId); } finally { geofencesLock.readLock().unlock(); } } public final void updateGeofence(Geofence geofence) { geofencesLock.writeLock().lock(); try { geofences.put(geofence.getId(), geofence); } finally { geofencesLock.writeLock().unlock(); } try { dataManager.updateGeofence(geofence); } catch (SQLException error) { Log.warning(error); } } public boolean checkGeofence(long userId, long geofenceId) { return getUserGeofencesIds(userId).contains(geofenceId); } public List getCurrentDeviceGeofences(Position position) { List result = new ArrayList(); for (Long geofenceId : getAllDeviceGeofences(position.getDeviceId())) { if (getGeofence(geofenceId).getGeometry().containsPoint(position.getLatitude(), position.getLongitude())) { result.add(geofenceId); } } return result; } }