From 9519b653a8cef055366b0903fdd371c8f86d1206 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Tue, 19 Sep 2017 10:42:07 +0500 Subject: Implement per device Notifications --- src/org/traccar/Context.java | 3 + src/org/traccar/api/BaseObjectResource.java | 2 - .../traccar/api/resource/CommandTypeResource.java | 4 +- .../traccar/api/resource/NotificationResource.java | 34 ++-- src/org/traccar/api/resource/UserResource.java | 3 - src/org/traccar/database/CommandsManager.java | 16 +- src/org/traccar/database/DataManager.java | 3 + src/org/traccar/database/NotificationManager.java | 187 +++++---------------- src/org/traccar/database/PermissionsManager.java | 11 +- src/org/traccar/model/CommandType.java | 33 ---- src/org/traccar/model/Notification.java | 10 +- src/org/traccar/model/Typed.java | 33 ++++ 12 files changed, 117 insertions(+), 222 deletions(-) delete mode 100644 src/org/traccar/model/CommandType.java create mode 100644 src/org/traccar/model/Typed.java (limited to 'src') diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 210d52429..340eb742c 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -62,6 +62,7 @@ import org.traccar.model.Device; import org.traccar.model.Driver; import org.traccar.model.Geofence; import org.traccar.model.Group; +import org.traccar.model.Notification; import org.traccar.model.User; import org.traccar.geolocation.GoogleGeolocationProvider; import org.traccar.geolocation.GeolocationProvider; @@ -433,6 +434,8 @@ public final class Context { return (BaseObjectManager) driversManager; } else if (clazz.equals(Command.class)) { return (BaseObjectManager) commandsManager; + } else if (clazz.equals(Notification.class)) { + return (BaseObjectManager) notificationManager; } return null; } diff --git a/src/org/traccar/api/BaseObjectResource.java b/src/org/traccar/api/BaseObjectResource.java index f0f31a154..634957a49 100644 --- a/src/org/traccar/api/BaseObjectResource.java +++ b/src/org/traccar/api/BaseObjectResource.java @@ -111,8 +111,6 @@ public abstract class BaseObjectResource extends BaseResour if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) { Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); Context.getPermissionsManager().refreshAllExtendedPermissions(); - } else if (baseClass.equals(User.class) && Context.getNotificationManager() != null) { - Context.getNotificationManager().refresh(); } return Response.ok(entity).build(); } diff --git a/src/org/traccar/api/resource/CommandTypeResource.java b/src/org/traccar/api/resource/CommandTypeResource.java index 30f9300cb..0a904bd8a 100644 --- a/src/org/traccar/api/resource/CommandTypeResource.java +++ b/src/org/traccar/api/resource/CommandTypeResource.java @@ -18,7 +18,7 @@ package org.traccar.api.resource; import org.traccar.Context; import org.traccar.api.BaseResource; -import org.traccar.model.CommandType; +import org.traccar.model.Typed; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -34,7 +34,7 @@ import java.util.Collection; public class CommandTypeResource extends BaseResource { @GET - public Collection get(@QueryParam("deviceId") long deviceId, + public Collection get(@QueryParam("deviceId") long deviceId, @QueryParam("textChannel") boolean textChannel) { if (deviceId != 0) { Context.getPermissionsManager().checkDevice(getUserId(), deviceId); diff --git a/src/org/traccar/api/resource/NotificationResource.java b/src/org/traccar/api/resource/NotificationResource.java index dee972607..540f02926 100644 --- a/src/org/traccar/api/resource/NotificationResource.java +++ b/src/org/traccar/api/resource/NotificationResource.java @@ -15,7 +15,6 @@ */ package org.traccar.api.resource; -import java.sql.SQLException; import java.util.Collection; import javax.mail.MessagingException; @@ -24,14 +23,14 @@ import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.traccar.Context; -import org.traccar.api.BaseResource; +import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Event; import org.traccar.model.Notification; +import org.traccar.model.Typed; import org.traccar.notification.NotificationMail; import org.traccar.notification.NotificationSms; @@ -40,34 +39,23 @@ import com.cloudhopper.smpp.type.SmppChannelException; import com.cloudhopper.smpp.type.SmppTimeoutException; import com.cloudhopper.smpp.type.UnrecoverablePduException; -@Path("users/notifications") +@Path("notifications") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class NotificationResource extends BaseResource { +public class NotificationResource extends ExtendedObjectResource { - @GET - public Collection get(@QueryParam("all") boolean all, - @QueryParam("userId") long userId) throws SQLException { - if (all) { - return Context.getNotificationManager().getAllNotifications(); - } - if (userId == 0) { - userId = getUserId(); - } - Context.getPermissionsManager().checkUser(getUserId(), userId); - return Context.getNotificationManager().getAllUserNotifications(userId); + public NotificationResource() { + super(Notification.class); } - @POST - public Response update(Notification entity) throws SQLException { - Context.getPermissionsManager().checkReadonly(getUserId()); - Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId()); - Context.getNotificationManager().updateNotification(entity); - return Response.ok(entity).build(); + @GET + @Path("types") + public Collection get() { + return Context.getNotificationManager().getAllNotificationTypes(); } - @Path("test") @POST + @Path("test") public Response testMessage() throws MessagingException, RecoverablePduException, UnrecoverablePduException, SmppTimeoutException, SmppChannelException, InterruptedException { NotificationMail.sendMailSync(getUserId(), new Event("test", 0), null); diff --git a/src/org/traccar/api/resource/UserResource.java b/src/org/traccar/api/resource/UserResource.java index 0eb328ab5..0f6f6edba 100644 --- a/src/org/traccar/api/resource/UserResource.java +++ b/src/org/traccar/api/resource/UserResource.java @@ -85,9 +85,6 @@ public class UserResource extends BaseObjectResource { Context.getDataManager().linkObject(User.class, getUserId(), ManagedUser.class, entity.getId(), true); } Context.getUsersManager().refreshUserItems(); - if (Context.getNotificationManager() != null) { - Context.getNotificationManager().refresh(); - } return Response.ok(entity).build(); } diff --git a/src/org/traccar/database/CommandsManager.java b/src/org/traccar/database/CommandsManager.java index deb802b29..521a2e1d1 100644 --- a/src/org/traccar/database/CommandsManager.java +++ b/src/org/traccar/database/CommandsManager.java @@ -26,7 +26,7 @@ import org.traccar.BaseProtocol; import org.traccar.Context; import org.traccar.helper.Log; import org.traccar.model.Command; -import org.traccar.model.CommandType; +import org.traccar.model.Typed; import org.traccar.model.Position; public class CommandsManager extends ExtendedObjectManager { @@ -102,29 +102,29 @@ public class CommandsManager extends ExtendedObjectManager { return result; } - public Collection getCommandTypes(long deviceId, boolean textChannel) { - List result = new ArrayList<>(); + public Collection getCommandTypes(long deviceId, boolean textChannel) { + List result = new ArrayList<>(); Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId); if (lastPosition != null) { BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol()); Collection commands; commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands(); for (String commandKey : commands) { - result.add(new CommandType(commandKey)); + result.add(new Typed(commandKey)); } } else { - result.add(new CommandType(Command.TYPE_CUSTOM)); + result.add(new Typed(Command.TYPE_CUSTOM)); } return result; } - public Collection getAllCommandTypes() { - List result = new ArrayList<>(); + public Collection getAllCommandTypes() { + List 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 CommandType(field.get(null).toString())); + result.add(new Typed(field.get(null).toString())); } catch (IllegalArgumentException | IllegalAccessException error) { Log.warning(error); } diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index 261541b4d..e88ff7f0d 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -48,6 +48,7 @@ import org.traccar.model.Event; import org.traccar.model.Geofence; import org.traccar.model.Group; import org.traccar.model.ManagedUser; +import org.traccar.model.Notification; import org.traccar.model.Permission; import org.traccar.model.BaseModel; import org.traccar.model.Calendar; @@ -393,6 +394,8 @@ public class DataManager { return Calendar.class; case "command": return Command.class; + case "notification": + return Notification.class; default: throw new ClassNotFoundException(); } diff --git a/src/org/traccar/database/NotificationManager.java b/src/org/traccar/database/NotificationManager.java index 98cae3499..a6ca0ea66 100644 --- a/src/org/traccar/database/NotificationManager.java +++ b/src/org/traccar/database/NotificationManager.java @@ -1,5 +1,6 @@ /* * 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. @@ -18,57 +19,70 @@ package org.traccar.database; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.SQLException; -import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; 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.Event; import org.traccar.model.Notification; import org.traccar.model.Position; +import org.traccar.model.Typed; import org.traccar.notification.NotificationMail; import org.traccar.notification.NotificationSms; -public class NotificationManager { - - private final DataManager dataManager; - - private final Map> userNotifications = new HashMap<>(); - - private final ReadWriteLock notificationsLock = new ReentrantReadWriteLock(); +public class NotificationManager extends ExtendedObjectManager { public NotificationManager(DataManager dataManager) { - this.dataManager = dataManager; - refresh(); + super(dataManager, Notification.class); + } + + private Set getEffectiveNotifications(long userId, long deviceId) { + Set result = new HashSet<>(); + Set deviceNotifications = getAllDeviceItems(deviceId); + for (long itemId : getUserItems(userId)) { + if (getById(itemId).getAlways() || deviceNotifications.contains(itemId)) { + result.add(itemId); + } + } + return result; } public void updateEvent(Event event, Position position) { try { - dataManager.addObject(event); + getDataManager().addObject(event); } catch (SQLException error) { Log.warning(error); } - Set users = Context.getPermissionsManager().getDeviceUsers(event.getDeviceId()); + long deviceId = event.getDeviceId(); + Set users = Context.getPermissionsManager().getDeviceUsers(deviceId); for (long userId : users) { if (event.getGeofenceId() == 0 || Context.getGeofenceManager() != null && Context.getGeofenceManager().checkItemPermission(userId, event.getGeofenceId())) { - Notification notification = getUserNotificationByType(userId, event.getType()); - if (notification != null) { - if (notification.getWeb()) { - Context.getConnectionManager().updateEvent(userId, event); - } - if (notification.getMail()) { - NotificationMail.sendMailAsync(userId, event, position); + boolean webSent = false; + boolean mailSent = false; + boolean smsSent = Context.getSmppManager() == null; + for (long notificationId : getEffectiveNotifications(userId, deviceId)) { + Notification notification = getById(notificationId); + if (getById(notificationId).getType().equals(event.getType())) { + if (!webSent && notification.getWeb()) { + Context.getConnectionManager().updateEvent(userId, event); + webSent = true; + } + if (!mailSent && notification.getMail()) { + NotificationMail.sendMailAsync(userId, event, position); + mailSent = true; + } + if (!smsSent && notification.getSms()) { + NotificationSms.sendSmsAsync(userId, event, position); + smsSent = true; + } } - if (notification.getSms()) { - NotificationSms.sendSmsAsync(userId, event, position); + if (webSent && mailSent && smsSent) { + break; } } } @@ -84,135 +98,18 @@ public class NotificationManager { } } - private Set getUserNotificationsUnsafe(long userId) { - if (!userNotifications.containsKey(userId)) { - userNotifications.put(userId, new HashSet()); - } - return userNotifications.get(userId); - } - - public Set getUserNotifications(long userId) { - notificationsLock.readLock().lock(); - try { - return getUserNotificationsUnsafe(userId); - } finally { - notificationsLock.readLock().unlock(); - } - } - - public final void refresh() { - if (dataManager != null) { - try { - notificationsLock.writeLock().lock(); - try { - userNotifications.clear(); - for (Notification notification : dataManager.getObjects(Notification.class)) { - getUserNotificationsUnsafe(notification.getUserId()).add(notification); - } - } finally { - notificationsLock.writeLock().unlock(); - } - } catch (SQLException error) { - Log.warning(error); - } - } - } - - public Notification getUserNotificationByType(long userId, String type) { - notificationsLock.readLock().lock(); - try { - for (Notification notification : getUserNotificationsUnsafe(userId)) { - if (notification.getType().equals(type)) { - return notification; - } - } - } finally { - notificationsLock.readLock().unlock(); - } - return null; - } - - public void updateNotification(Notification notification) { - Notification cachedNotification = getUserNotificationByType(notification.getUserId(), notification.getType()); - if (cachedNotification != null) { - if (cachedNotification.getWeb() != notification.getWeb() - || cachedNotification.getMail() != notification.getMail() - || cachedNotification.getSms() != notification.getSms()) { - if (!notification.getWeb() && !notification.getMail() && !notification.getSms()) { - try { - dataManager.removeObject(Notification.class, cachedNotification.getId()); - } catch (SQLException error) { - Log.warning(error); - } - notificationsLock.writeLock().lock(); - try { - getUserNotificationsUnsafe(notification.getUserId()).remove(cachedNotification); - } finally { - notificationsLock.writeLock().unlock(); - } - } else { - notificationsLock.writeLock().lock(); - try { - cachedNotification.setWeb(notification.getWeb()); - cachedNotification.setMail(notification.getMail()); - cachedNotification.setSms(notification.getSms()); - cachedNotification.setAttributes(notification.getAttributes()); - } finally { - notificationsLock.writeLock().unlock(); - } - try { - dataManager.updateObject(cachedNotification); - } catch (SQLException error) { - Log.warning(error); - } - } - } else { - notification.setId(cachedNotification.getId()); - } - } else if (notification.getWeb() || notification.getMail() || notification.getSms()) { - try { - dataManager.addObject(notification); - } catch (SQLException error) { - Log.warning(error); - } - notificationsLock.writeLock().lock(); - try { - getUserNotificationsUnsafe(notification.getUserId()).add(notification); - } finally { - notificationsLock.writeLock().unlock(); - } - } - } - - public Set getAllNotifications() { - Set notifications = new HashSet<>(); - long id = 1; + public Set getAllNotificationTypes() { + Set types = new HashSet<>(); Field[] fields = Event.class.getDeclaredFields(); for (Field field : fields) { if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) { try { - Notification notification = new Notification(); - notification.setType(field.get(null).toString()); - notification.setId(id++); - notifications.add(notification); + types.add(new Typed(field.get(null).toString())); } catch (IllegalArgumentException | IllegalAccessException error) { Log.warning(error); } } } - return notifications; + return types; } - - public Collection getAllUserNotifications(long userId) { - Map notifications = new HashMap<>(); - for (Notification notification : getAllNotifications()) { - notification.setUserId(userId); - notifications.put(notification.getType(), notification); - } - for (Notification notification : getUserNotifications(userId)) { - notifications.put(notification.getType(), notification); - } - return notifications.values(); - } - } diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java index 0d9c780a6..07b60ba58 100644 --- a/src/org/traccar/database/PermissionsManager.java +++ b/src/org/traccar/database/PermissionsManager.java @@ -26,6 +26,7 @@ import org.traccar.model.Driver; import org.traccar.model.Geofence; import org.traccar.model.Group; import org.traccar.model.ManagedUser; +import org.traccar.model.Notification; import org.traccar.model.Permission; import org.traccar.model.Server; import org.traccar.model.User; @@ -320,6 +321,8 @@ public class PermissionsManager { manager = Context.getCalendarManager(); } else if (object.equals(Command.class)) { manager = Context.getCommandsManager(); + } else if (object.equals(Notification.class)) { + manager = Context.getNotificationManager(); } else { throw new IllegalArgumentException("Unknown object type"); } @@ -344,7 +347,7 @@ public class PermissionsManager { Context.getAttributesManager().refreshUserItems(); Context.getCommandsManager().refreshUserItems(); if (Context.getNotificationManager() != null) { - Context.getNotificationManager().refresh(); + Context.getNotificationManager().refreshUserItems(); } } @@ -375,6 +378,9 @@ public class PermissionsManager { Context.getCalendarManager().refreshUserItems(); } else if (permission.getPropertyClass().equals(Command.class)) { Context.getCommandsManager().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) { @@ -385,6 +391,9 @@ public class PermissionsManager { Context.getAttributesManager().refreshExtendedPermissions(); } else if (permission.getPropertyClass().equals(Command.class)) { Context.getCommandsManager().refreshExtendedPermissions(); + } else if (permission.getPropertyClass().equals(Notification.class) + && Context.getNotificationManager() != null) { + Context.getNotificationManager().refreshExtendedPermissions(); } } } diff --git a/src/org/traccar/model/CommandType.java b/src/org/traccar/model/CommandType.java deleted file mode 100644 index 210316f71..000000000 --- a/src/org/traccar/model/CommandType.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016 Gabor Somogyi (gabor.g.somogyi@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.model; - -public class CommandType { - - private String type; - - public CommandType(String type) { - this.type = type; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } -} diff --git a/src/org/traccar/model/Notification.java b/src/org/traccar/model/Notification.java index e7bb69903..9d6034fff 100644 --- a/src/org/traccar/model/Notification.java +++ b/src/org/traccar/model/Notification.java @@ -17,14 +17,14 @@ package org.traccar.model; public class Notification extends ExtendedModel { - private long userId; + private boolean always; - public long getUserId() { - return userId; + public boolean getAlways() { + return always; } - public void setUserId(long userId) { - this.userId = userId; + public void setAlways(boolean always) { + this.always = always; } private String type; diff --git a/src/org/traccar/model/Typed.java b/src/org/traccar/model/Typed.java new file mode 100644 index 000000000..313ec7bcd --- /dev/null +++ b/src/org/traccar/model/Typed.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Gabor Somogyi (gabor.g.somogyi@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.model; + +public class Typed { + + private String type; + + public Typed(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} -- cgit v1.2.3