diff options
author | Anton Tananaev <anton.tananaev@gmail.com> | 2016-06-18 11:56:59 +1200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-06-18 11:56:59 +1200 |
commit | b3b0cd9d6f2d26cef2e64ef38e23203f1f3fa51a (patch) | |
tree | 2e8deb61d372095aebb8270f740501c8124ea8c4 /src/org/traccar/database | |
parent | d801cba474cd05bb088348f04e8557ca638cd74f (diff) | |
parent | 82a78ff77a076231a8429f0dd84678d61c31d44a (diff) | |
download | trackermap-server-b3b0cd9d6f2d26cef2e64ef38e23203f1f3fa51a.tar.gz trackermap-server-b3b0cd9d6f2d26cef2e64ef38e23203f1f3fa51a.tar.bz2 trackermap-server-b3b0cd9d6f2d26cef2e64ef38e23203f1f3fa51a.zip |
Merge pull request #2012 from Abyss777/master
Implement Geofences on server side
Diffstat (limited to 'src/org/traccar/database')
-rw-r--r-- | src/org/traccar/database/ConnectionManager.java | 20 | ||||
-rw-r--r-- | src/org/traccar/database/DataManager.java | 126 | ||||
-rw-r--r-- | src/org/traccar/database/GeofenceManager.java | 227 | ||||
-rw-r--r-- | src/org/traccar/database/NotificationManager.java | 57 | ||||
-rw-r--r-- | src/org/traccar/database/PermissionsManager.java | 9 |
5 files changed, 421 insertions, 18 deletions
diff --git a/src/org/traccar/database/ConnectionManager.java b/src/org/traccar/database/ConnectionManager.java index ec5903548..8a1debdfa 100644 --- a/src/org/traccar/database/ConnectionManager.java +++ b/src/org/traccar/database/ConnectionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,7 +94,7 @@ public class ConnectionManager { if (status.equals(Device.STATUS_ONLINE)) { event.setType(Event.TYPE_DEVICE_ONLINE); } - updateEvent(event, null); + Context.getNotificationManager().updateEvent(event, null); } device.setStatus(status); @@ -147,18 +147,10 @@ public class ConnectionManager { } } - public synchronized void updateEvent(Event event, Position position) { - long deviceId = event.getDeviceId(); - try { - Context.getDataManager().addEvent(event); - } catch (SQLException error) { - Log.warning(error); - } - for (long userId : Context.getPermissionsManager().getDeviceUsers(deviceId)) { - if (listeners.containsKey(userId)) { - for (UpdateListener listener : listeners.get(userId)) { - listener.onUpdateEvent(event, position); - } + public synchronized void updateEvent(long userId, Event event, Position position) { + if (listeners.containsKey(userId)) { + for (UpdateListener listener : listeners.get(userId)) { + listener.onUpdateEvent(event, position); } } } diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index 67b7d1e55..860bf1b84 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -48,11 +48,15 @@ import org.traccar.helper.Log; import org.traccar.model.Device; import org.traccar.model.DevicePermission; import org.traccar.model.Event; +import org.traccar.model.Geofence; import org.traccar.model.Group; +import org.traccar.model.GroupGeofence; import org.traccar.model.GroupPermission; import org.traccar.model.Position; import org.traccar.model.Server; import org.traccar.model.User; +import org.traccar.model.DeviceGeofence; +import org.traccar.model.GeofencePermission; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -146,9 +150,17 @@ public class DataManager implements IdentityManager { if (force || System.currentTimeMillis() - devicesLastUpdate > dataRefreshDelay) { devicesById.clear(); devicesByUniqueId.clear(); + ConnectionManager connectionManager = Context.getConnectionManager(); + GeofenceManager geofenceManager = Context.getGeofenceManager(); for (Device device : getAllDevices()) { devicesById.put(device.getId(), device); devicesByUniqueId.put(device.getUniqueId(), device); + if (connectionManager != null && geofenceManager != null) { + Position lastPosition = connectionManager.getLastPosition(device.getId()); + if (lastPosition != null) { + device.setGeofenceIds(geofenceManager.getCurrentDeviceGeofences(lastPosition)); + } + } } devicesLastUpdate = System.currentTimeMillis(); } @@ -334,11 +346,34 @@ public class DataManager implements IdentityManager { .executeQuery(GroupPermission.class); } - public Collection<Device> getAllDevices() throws SQLException { + private Collection<Device> getAllDevices() throws SQLException { return QueryBuilder.create(dataSource, getQuery("database.selectDevicesAll")) .executeQuery(Device.class); } + public Collection<Device> getAllDevicesCached() { + boolean forceUpdate; + devicesLock.readLock().lock(); + try { + forceUpdate = devicesById.values().isEmpty(); + } finally { + devicesLock.readLock().unlock(); + } + + try { + updateDeviceCache(forceUpdate); + } catch (SQLException e) { + Log.warning(e); + } + + devicesLock.readLock().lock(); + try { + return devicesById.values(); + } finally { + devicesLock.readLock().unlock(); + } + } + public Collection<Device> getDevices(long userId) throws SQLException { Collection<Device> devices = new ArrayList<>(); for (long id : Context.getPermissionsManager().getDevicePermissions(userId)) { @@ -520,8 +555,93 @@ public class DataManager implements IdentityManager { public Collection<Event> getLastEvents(long deviceId, String type, int interval) throws SQLException { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.SECOND, -interval); - Date to = calendar.getTime(); - return getEvents(deviceId, type, new Date(), to); + Date from = calendar.getTime(); + return getEvents(deviceId, type, from, new Date()); + } + + public Collection<Geofence> getGeofences() throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectGeofencesAll")) + .executeQuery(Geofence.class); + } + + public Geofence getGeofence(long geofenceId) throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectGeofences")) + .setLong("id", geofenceId) + .executeQuerySingle(Geofence.class); + } + + public void addGeofence(Geofence geofence) throws SQLException { + geofence.setId(QueryBuilder.create(dataSource, getQuery("database.insertGeofence"), true) + .setObject(geofence) + .executeUpdate()); + } + + public void updateGeofence(Geofence geofence) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.updateGeofence")) + .setObject(geofence) + .executeUpdate(); + } + + public void removeGeofence(long geofenceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.deleteGeofence")) + .setLong("id", geofenceId) + .executeUpdate(); + } + + public Collection<GeofencePermission> getGeofencePermissions() throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectGeofencePermissions")) + .executeQuery(GeofencePermission.class); + } + + public void linkGeofence(long userId, long geofenceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.linkGeofence")) + .setLong("userId", userId) + .setLong("geofenceId", geofenceId) + .executeUpdate(); } + public void unlinkGeofence(long userId, long geofenceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.unlinkGeofence")) + .setLong("userId", userId) + .setLong("geofenceId", geofenceId) + .executeUpdate(); + } + + public Collection<GroupGeofence> getGroupGeofences() throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectGroupGeofences")) + .executeQuery(GroupGeofence.class); + } + + public void linkGroupGeofence(long groupId, long geofenceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.linkGroupGeofence")) + .setLong("groupId", groupId) + .setLong("geofenceId", geofenceId) + .executeUpdate(); + } + + public void unlinkGroupGeofence(long groupId, long geofenceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.unlinkGroupGeofence")) + .setLong("groupId", groupId) + .setLong("geofenceId", geofenceId) + .executeUpdate(); + } + + public Collection<DeviceGeofence> getDeviceGeofences() throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectDeviceGeofences")) + .executeQuery(DeviceGeofence.class); + } + + public void linkDeviceGeofence(long deviceId, long geofenceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.linkDeviceGeofence")) + .setLong("deviceId", deviceId) + .setLong("geofenceId", geofenceId) + .executeUpdate(); + } + + public void unlinkDeviceGeofence(long deviceId, long geofenceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.unlinkDeviceGeofence")) + .setLong("deviceId", deviceId) + .setLong("geofenceId", geofenceId) + .executeUpdate(); + } } diff --git a/src/org/traccar/database/GeofenceManager.java b/src/org/traccar/database/GeofenceManager.java new file mode 100644 index 000000000..0a07e2cf3 --- /dev/null +++ b/src/org/traccar/database/GeofenceManager.java @@ -0,0 +1,227 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.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<Long, Geofence> geofences = new HashMap<>(); + private final Map<Long, Set<Long>> userGeofences = new HashMap<>(); + private final Map<Long, Set<Long>> groupGeofences = new HashMap<>(); + + private final Map<Long, Set<Long>> deviceGeofencesWithGroups = new HashMap<>(); + private final Map<Long, Set<Long>> 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<Long> getUserGeofencesIds(long userId) { + if (!userGeofences.containsKey(userId)) { + userGeofences.put(userId, new HashSet<Long>()); + } + return userGeofences.get(userId); + } + + private Set<Long> getGroupGeofences(long groupId) { + if (!groupGeofences.containsKey(groupId)) { + groupGeofences.put(groupId, new HashSet<Long>()); + } + return groupGeofences.get(groupId); + } + + public Set<Long> getAllDeviceGeofences(long deviceId) { + deviceGeofencesLock.readLock().lock(); + try { + return getDeviceGeofences(deviceGeofencesWithGroups, deviceId); + } finally { + deviceGeofencesLock.readLock().unlock(); + } + + } + + public Set<Long> getDeviceGeofences(long deviceId) { + deviceGeofencesLock.readLock().lock(); + try { + return getDeviceGeofences(deviceGeofences, deviceId); + } finally { + deviceGeofencesLock.readLock().unlock(); + } + } + + private Set<Long> getDeviceGeofences(Map<Long, Set<Long>> deviceGeofences, long deviceId) { + if (!deviceGeofences.containsKey(deviceId)) { + deviceGeofences.put(deviceId, new HashSet<Long>()); + } + 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<Long> deviceGeofenceIds = device.getGeofenceIds(); + if (deviceGeofenceIds == null) { + deviceGeofenceIds = new ArrayList<Long>(); + } 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<Geofence> getAllGeofences() { + geofencesLock.readLock().lock(); + try { + return geofences.values(); + } finally { + geofencesLock.readLock().unlock(); + } + } + + public final Collection<Geofence> getUserGeofences(long userId) { + geofencesLock.readLock().lock(); + try { + Collection<Geofence> 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<Long> getCurrentDeviceGeofences(Position position) { + List<Long> result = new ArrayList<Long>(); + for (Long geofenceId : getAllDeviceGeofences(position.getDeviceId())) { + if (getGeofence(geofenceId).getGeometry().containsPoint(position.getLatitude(), position.getLongitude())) { + result.add(geofenceId); + } + } + return result; + } + +} diff --git a/src/org/traccar/database/NotificationManager.java b/src/org/traccar/database/NotificationManager.java new file mode 100644 index 000000000..7593367a3 --- /dev/null +++ b/src/org/traccar/database/NotificationManager.java @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.Set; + +import org.traccar.Context; +import org.traccar.helper.Log; +import org.traccar.model.Event; +import org.traccar.model.Position; + +public class NotificationManager { + + private final DataManager dataManager; + + public NotificationManager(DataManager dataManager) { + this.dataManager = dataManager; + } + + public void updateEvent(Event event, Position position) { + try { + dataManager.addEvent(event); + } catch (SQLException error) { + Log.warning(error); + } + + Set<Long> users = Context.getPermissionsManager().getDeviceUsers(event.getDeviceId()); + for (Long userId : users) { + if (event.getGeofenceId() == 0 + || Context.getGeofenceManager().checkGeofence(userId, event.getGeofenceId())) { + Context.getConnectionManager().updateEvent(userId, event, position); + } + } + } + + public void updateEvents(Collection<Event> events, Position position) { + + for (Event event : events) { + updateEvent(event, position); + } + } +} diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java index 08d44b382..b6dd2e2a9 100644 --- a/src/org/traccar/database/PermissionsManager.java +++ b/src/org/traccar/database/PermissionsManager.java @@ -15,6 +15,7 @@ */ package org.traccar.database; +import org.traccar.Context; import org.traccar.helper.Log; import org.traccar.model.Device; import org.traccar.model.DevicePermission; @@ -77,7 +78,7 @@ public class PermissionsManager { users.put(user.getId(), user); } - GroupTree groupTree = new GroupTree(dataManager.getAllGroups(), dataManager.getAllDevices()); + GroupTree groupTree = new GroupTree(dataManager.getAllGroups(), dataManager.getAllDevicesCached()); for (GroupPermission permission : dataManager.getGroupPermissions()) { Set<Long> userGroupPermissions = getGroupPermissions(permission.getUserId()); Set<Long> userDevicePermissions = getDevicePermissions(permission.getUserId()); @@ -145,4 +146,10 @@ public class PermissionsManager { } } + public void checkGeofence(long userId, long geofenceId) throws SecurityException { + if (!Context.getGeofenceManager().checkGeofence(userId, geofenceId) && !isAdmin(userId)) { + throw new SecurityException("Geofence access denied"); + } + } + } |