aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar/database
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2016-06-18 11:56:59 +1200
committerGitHub <noreply@github.com>2016-06-18 11:56:59 +1200
commitb3b0cd9d6f2d26cef2e64ef38e23203f1f3fa51a (patch)
tree2e8deb61d372095aebb8270f740501c8124ea8c4 /src/org/traccar/database
parentd801cba474cd05bb088348f04e8557ca638cd74f (diff)
parent82a78ff77a076231a8429f0dd84678d61c31d44a (diff)
downloadtraccar-server-b3b0cd9d6f2d26cef2e64ef38e23203f1f3fa51a.tar.gz
traccar-server-b3b0cd9d6f2d26cef2e64ef38e23203f1f3fa51a.tar.bz2
traccar-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.java20
-rw-r--r--src/org/traccar/database/DataManager.java126
-rw-r--r--src/org/traccar/database/GeofenceManager.java227
-rw-r--r--src/org/traccar/database/NotificationManager.java57
-rw-r--r--src/org/traccar/database/PermissionsManager.java9
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");
+ }
+ }
+
}