aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2017-05-13 11:01:48 +1200
committerGitHub <noreply@github.com>2017-05-13 11:01:48 +1200
commit713d6c55a007aa80850810f447308976516bfa63 (patch)
tree057e9147e724a56b6e2f3820fcf3d9ab67561fdf /src/org/traccar
parent51920cae6438f8888090f177761a82afff33067f (diff)
parent6c17f85d04b224ff2a09265918765c9f4fc8cf94 (diff)
downloadtraccar-server-713d6c55a007aa80850810f447308976516bfa63.tar.gz
traccar-server-713d6c55a007aa80850810f447308976516bfa63.tar.bz2
traccar-server-713d6c55a007aa80850810f447308976516bfa63.zip
Merge pull request #3156 from Abyss777/computed_attributes
Implement computed attributes
Diffstat (limited to 'src/org/traccar')
-rw-r--r--src/org/traccar/BasePipelineFactory.java13
-rw-r--r--src/org/traccar/Context.java9
-rw-r--r--src/org/traccar/api/resource/AttributePermissionResource.java58
-rw-r--r--src/org/traccar/api/resource/AttributeResource.java111
-rw-r--r--src/org/traccar/api/resource/DeviceAttributeResource.java58
-rw-r--r--src/org/traccar/api/resource/GroupAttributeResource.java58
-rw-r--r--src/org/traccar/database/AttributesManager.java199
-rw-r--r--src/org/traccar/database/DataManager.java84
-rw-r--r--src/org/traccar/database/PermissionsManager.java12
-rw-r--r--src/org/traccar/model/Attribute.java71
-rw-r--r--src/org/traccar/model/AttributePermission.java41
-rw-r--r--src/org/traccar/model/DeviceAttribute.java40
-rw-r--r--src/org/traccar/model/GroupAttribute.java40
-rw-r--r--src/org/traccar/processing/ComputedAttributesHandler.java78
-rw-r--r--src/org/traccar/processing/CopyAttributesHandler.java (renamed from src/org/traccar/CopyAttributesHandler.java)8
15 files changed, 876 insertions, 4 deletions
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
index 620c4729a..11457905d 100644
--- a/src/org/traccar/BasePipelineFactory.java
+++ b/src/org/traccar/BasePipelineFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -38,6 +38,8 @@ import org.traccar.events.MotionEventHandler;
import org.traccar.events.OverspeedEventHandler;
import org.traccar.events.AlertEventHandler;
import org.traccar.helper.Log;
+import org.traccar.processing.ComputedAttributesHandler;
+import org.traccar.processing.CopyAttributesHandler;
import java.net.InetSocketAddress;
@@ -53,6 +55,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
private GeolocationHandler geolocationHandler;
private HemisphereHandler hemisphereHandler;
private CopyAttributesHandler copyAttributesHandler;
+ private ComputedAttributesHandler computedAttributesHandler;
private CommandResultEventHandler commandResultEventHandler;
private OverspeedEventHandler overspeedEventHandler;
@@ -153,6 +156,10 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
copyAttributesHandler = new CopyAttributesHandler();
}
+ if (Context.getConfig().getBoolean("processing.computedAttributes.enable")) {
+ computedAttributesHandler = new ComputedAttributesHandler();
+ }
+
if (Context.getConfig().getBoolean("event.enable")) {
commandResultEventHandler = new CommandResultEventHandler();
overspeedEventHandler = new OverspeedEventHandler();
@@ -209,6 +216,10 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
pipeline.addLast("copyAttributes", copyAttributesHandler);
}
+ if (computedAttributesHandler != null) {
+ pipeline.addLast("computedAttributes", computedAttributesHandler);
+ }
+
if (Context.getDataManager() != null) {
pipeline.addLast("dataHandler", new DefaultDataHandler());
}
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index bfef3807b..07ede4cad 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -27,6 +27,7 @@ import org.apache.velocity.app.VelocityEngine;
import org.eclipse.jetty.util.URIUtil;
import org.traccar.database.AliasesManager;
import org.traccar.database.CalendarManager;
+import org.traccar.database.AttributesManager;
import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
import org.traccar.database.DeviceManager;
@@ -173,6 +174,12 @@ public final class Context {
return aliasesManager;
}
+ private static AttributesManager attributesManager;
+
+ public static AttributesManager getAttributesManager() {
+ return attributesManager;
+ }
+
private static StatisticsManager statisticsManager;
public static StatisticsManager getStatisticsManager() {
@@ -310,6 +317,8 @@ public final class Context {
aliasesManager = new AliasesManager(dataManager);
+ attributesManager = new AttributesManager(dataManager);
+
statisticsManager = new StatisticsManager();
if (config.getBoolean("sms.smpp.enable")) {
diff --git a/src/org/traccar/api/resource/AttributePermissionResource.java b/src/org/traccar/api/resource/AttributePermissionResource.java
new file mode 100644
index 000000000..1924bcdf1
--- /dev/null
+++ b/src/org/traccar/api/resource/AttributePermissionResource.java
@@ -0,0 +1,58 @@
+/*
+ * 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.api.resource;
+
+import java.sql.SQLException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.AttributePermission;
+
+@Path("permissions/attributes")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class AttributePermissionResource extends BaseResource {
+
+ @POST
+ public Response add(AttributePermission entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
+ Context.getDataManager().linkAttribute(entity.getUserId(), entity.getAttributeId());
+ Context.getAttributesManager().refreshUserAttributes();
+ return Response.ok(entity).build();
+ }
+
+ @DELETE
+ public Response remove(AttributePermission entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
+ Context.getDataManager().unlinkAttribute(entity.getUserId(), entity.getAttributeId());
+ Context.getAttributesManager().refreshUserAttributes();
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/AttributeResource.java b/src/org/traccar/api/resource/AttributeResource.java
new file mode 100644
index 000000000..7751a9360
--- /dev/null
+++ b/src/org/traccar/api/resource/AttributeResource.java
@@ -0,0 +1,111 @@
+/*
+ * 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.api.resource;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+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.database.AttributesManager;
+import org.traccar.model.Attribute;
+
+@Path("attributes/computed")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class AttributeResource extends BaseResource {
+
+ @GET
+ public Collection<Attribute> get(
+ @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId,
+ @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException {
+
+ AttributesManager attributesManager = Context.getAttributesManager();
+ if (refresh) {
+ attributesManager.refreshAttributes();
+ }
+
+ Set<Long> result = new HashSet<>();
+ if (all) {
+ if (Context.getPermissionsManager().isAdmin(getUserId())) {
+ result.addAll(attributesManager.getAllAttributes());
+ } else {
+ Context.getPermissionsManager().checkManager(getUserId());
+ result.addAll(attributesManager.getManagedAttributes(getUserId()));
+ }
+ } else {
+ if (userId == 0) {
+ userId = getUserId();
+ }
+ Context.getPermissionsManager().checkUser(getUserId(), userId);
+ result.addAll(attributesManager.getUserAttributes(userId));
+ }
+
+ if (groupId != 0) {
+ Context.getPermissionsManager().checkGroup(getUserId(), groupId);
+ result.retainAll(attributesManager.getGroupAttributes(groupId));
+ }
+
+ if (deviceId != 0) {
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ result.retainAll(attributesManager.getDeviceAttributes(deviceId));
+ }
+ return attributesManager.getAttributes(result);
+
+ }
+ @POST
+ public Response add(Attribute entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getAttributesManager().addAttribute(entity);
+ Context.getDataManager().linkAttribute(getUserId(), entity.getId());
+ Context.getAttributesManager().refreshUserAttributes();
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @PUT
+ public Response update(Attribute entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkAttribute(getUserId(), entity.getId());
+ Context.getAttributesManager().updateAttribute(entity);
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @DELETE
+ public Response remove(@PathParam("id") long id) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkAttribute(getUserId(), id);
+ Context.getAttributesManager().removeAttribute(id);
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/DeviceAttributeResource.java b/src/org/traccar/api/resource/DeviceAttributeResource.java
new file mode 100644
index 000000000..8d80c9235
--- /dev/null
+++ b/src/org/traccar/api/resource/DeviceAttributeResource.java
@@ -0,0 +1,58 @@
+/*
+ * 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.api.resource;
+
+import java.sql.SQLException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.DeviceAttribute;
+
+@Path("devices/attributes")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class DeviceAttributeResource extends BaseResource {
+
+ @POST
+ public Response add(DeviceAttribute entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
+ Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
+ Context.getDataManager().linkDeviceAttribute(entity.getDeviceId(), entity.getAttributeId());
+ Context.getAttributesManager().refresh();
+ return Response.ok(entity).build();
+ }
+
+ @DELETE
+ public Response remove(DeviceAttribute entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
+ Context.getPermissionsManager().checkGeofence(getUserId(), entity.getAttributeId());
+ Context.getDataManager().unlinkDeviceAttribute(entity.getDeviceId(), entity.getAttributeId());
+ Context.getAttributesManager().refresh();
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/GroupAttributeResource.java b/src/org/traccar/api/resource/GroupAttributeResource.java
new file mode 100644
index 000000000..84b876d34
--- /dev/null
+++ b/src/org/traccar/api/resource/GroupAttributeResource.java
@@ -0,0 +1,58 @@
+/*
+ * 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.api.resource;
+
+import java.sql.SQLException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.GroupAttribute;
+
+@Path("groups/attributes")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class GroupAttributeResource extends BaseResource {
+
+ @POST
+ public Response add(GroupAttribute entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
+ Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
+ Context.getDataManager().linkGroupAttribute(entity.getGroupId(), entity.getAttributeId());
+ Context.getAttributesManager().refresh();
+ return Response.ok(entity).build();
+ }
+
+ @DELETE
+ public Response remove(GroupAttribute entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
+ Context.getPermissionsManager().checkGeofence(getUserId(), entity.getAttributeId());
+ Context.getDataManager().unlinkGroupAttribute(entity.getGroupId(), entity.getAttributeId());
+ Context.getAttributesManager().refresh();
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/database/AttributesManager.java b/src/org/traccar/database/AttributesManager.java
new file mode 100644
index 000000000..362d6130f
--- /dev/null
+++ b/src/org/traccar/database/AttributesManager.java
@@ -0,0 +1,199 @@
+/*
+ * 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.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 org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.AttributePermission;
+import org.traccar.model.Attribute;
+import org.traccar.model.Device;
+import org.traccar.model.DeviceAttribute;
+import org.traccar.model.GroupAttribute;
+
+public class AttributesManager {
+
+ private final DataManager dataManager;
+
+ private final Map<Long, Attribute> attributes = new ConcurrentHashMap<>();
+ private final Map<Long, Set<Long>> deviceAttributes = new ConcurrentHashMap<>();
+ private final Map<Long, Set<Long>> deviceAttributesWithGroups = new ConcurrentHashMap<>();
+ private final Map<Long, Set<Long>> groupAttributes = new ConcurrentHashMap<>();
+ private final Map<Long, Set<Long>> userAttributes = new ConcurrentHashMap<>();
+
+ public AttributesManager(DataManager dataManager) {
+ this.dataManager = dataManager;
+ refreshAttributes();
+ }
+
+ public Set<Long> getUserAttributes(long userId) {
+ if (!userAttributes.containsKey(userId)) {
+ userAttributes.put(userId, new HashSet<Long>());
+ }
+ return userAttributes.get(userId);
+ }
+
+ public Set<Long> getGroupAttributes(long groupId) {
+ if (!groupAttributes.containsKey(groupId)) {
+ groupAttributes.put(groupId, new HashSet<Long>());
+ }
+ return groupAttributes.get(groupId);
+ }
+
+ public Set<Long> getDeviceAttributes(long deviceId) {
+ return getDeviceAttributes(deviceAttributes, deviceId);
+ }
+
+ public Set<Long> getAllDeviceAttributes(long deviceId) {
+ return getDeviceAttributes(deviceAttributesWithGroups, deviceId);
+ }
+
+ private Set<Long> getDeviceAttributes(Map<Long, Set<Long>> deviceAttributes, long deviceId) {
+ if (!deviceAttributes.containsKey(deviceId)) {
+ deviceAttributes.put(deviceId, new HashSet<Long>());
+ }
+ return deviceAttributes.get(deviceId);
+ }
+
+ public final void refreshAttributes() {
+ if (dataManager != null) {
+ try {
+ attributes.clear();
+ for (Attribute attribute : dataManager.getAttributes()) {
+ attributes.put(attribute.getId(), attribute);
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ refreshUserAttributes();
+ refresh();
+ }
+
+ public final void refreshUserAttributes() {
+ if (dataManager != null) {
+ try {
+ userAttributes.clear();
+ for (AttributePermission attributePermission : dataManager.getAttributePermissions()) {
+ getUserAttributes(attributePermission.getUserId()).add(attributePermission.getAttributeId());
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ }
+
+ public final void refresh() {
+ if (dataManager != null) {
+ try {
+
+ Collection<GroupAttribute> databaseGroupAttributes = dataManager.getGroupAttributes();
+
+ groupAttributes.clear();
+ for (GroupAttribute groupAttribute : databaseGroupAttributes) {
+ getGroupAttributes(groupAttribute.getGroupId()).add(groupAttribute.getAttributeId());
+ }
+
+ Collection<DeviceAttribute> databaseDeviceAttributes = dataManager.getDeviceAttributes();
+ Collection<Device> allDevices = Context.getDeviceManager().getAllDevices();
+
+ deviceAttributes.clear();
+ deviceAttributesWithGroups.clear();
+
+ for (DeviceAttribute deviceAttribute : databaseDeviceAttributes) {
+ getDeviceAttributes(deviceAttribute.getDeviceId())
+ .add(deviceAttribute.getAttributeId());
+ getAllDeviceAttributes(deviceAttribute.getDeviceId())
+ .add(deviceAttribute.getAttributeId());
+ }
+
+ for (Device device : allDevices) {
+ long groupId = device.getGroupId();
+ while (groupId != 0) {
+ getAllDeviceAttributes(device.getId()).addAll(getGroupAttributes(groupId));
+ if (Context.getDeviceManager().getGroupById(groupId) != null) {
+ groupId = Context.getDeviceManager().getGroupById(groupId).getGroupId();
+ } else {
+ groupId = 0;
+ }
+ }
+ }
+
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ }
+
+ public void addAttribute(Attribute attribute) throws SQLException {
+ dataManager.addAttribute(attribute);
+ attributes.put(attribute.getId(), attribute);
+ }
+
+ public void updateAttribute(Attribute attribute) throws SQLException {
+ dataManager.updateAttribute(attribute);
+ Attribute cachedAttribute = attributes.get(attribute.getId());
+ cachedAttribute.setDescription(attribute.getDescription());
+ cachedAttribute.setAttribute(attribute.getAttribute());
+ cachedAttribute.setExpression(attribute.getExpression());
+ cachedAttribute.setType(attribute.getType());
+ }
+
+ public void removeAttribute(long computedAttributeId) throws SQLException {
+ dataManager.removeAttribute(computedAttributeId);
+ attributes.remove(computedAttributeId);
+ refreshUserAttributes();
+ refresh();
+ }
+
+ public boolean checkAttribute(long userId, long attributeId) {
+ return getUserAttributes(userId).contains(attributeId);
+ }
+
+ public Attribute getAttribute(long id) {
+ return attributes.get(id);
+ }
+
+ public final Collection<Attribute> getAttributes(Set<Long> attributeIds) {
+ Collection<Attribute> result = new LinkedList<>();
+ for (long attributeId : attributeIds) {
+ result.add(getAttribute(attributeId));
+ }
+ return result;
+ }
+
+ public final Set<Long> getAllAttributes() {
+ return attributes.keySet();
+ }
+
+ public final Set<Long> getManagedAttributes(long userId) {
+ Set<Long> attributes = new HashSet<>();
+ attributes.addAll(getUserAttributes(userId));
+ for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
+ attributes.addAll(getUserAttributes(managedUserId));
+ }
+ return attributes;
+ }
+
+}
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index 2f2cf1275..840a8931c 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -37,13 +37,17 @@ import liquibase.resource.ResourceAccessor;
import org.traccar.Config;
import org.traccar.helper.Log;
import org.traccar.model.AttributeAlias;
+import org.traccar.model.AttributePermission;
import org.traccar.model.Calendar;
import org.traccar.model.CalendarPermission;
+import org.traccar.model.Attribute;
import org.traccar.model.Device;
+import org.traccar.model.DeviceAttribute;
import org.traccar.model.DevicePermission;
import org.traccar.model.Event;
import org.traccar.model.Geofence;
import org.traccar.model.Group;
+import org.traccar.model.GroupAttribute;
import org.traccar.model.GroupGeofence;
import org.traccar.model.GroupPermission;
import org.traccar.model.Notification;
@@ -552,4 +556,84 @@ public class DataManager {
.executeUpdate();
}
+ public Collection<Attribute> getAttributes() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectAttributes"))
+ .executeQuery(Attribute.class);
+ }
+
+ public void addAttribute(Attribute attribute) throws SQLException {
+ attribute.setId(QueryBuilder.create(dataSource, getQuery("database.insertAttribute"), true)
+ .setObject(attribute)
+ .executeUpdate());
+ }
+
+ public void updateAttribute(Attribute attribute) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.updateAttribute"))
+ .setObject(attribute)
+ .executeUpdate();
+ }
+
+ public void removeAttribute(long computedAttributeId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.deleteAttribute"))
+ .setLong("id", computedAttributeId)
+ .executeUpdate();
+ }
+
+ public Collection<AttributePermission> getAttributePermissions() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectAttributePermissions"))
+ .executeQuery(AttributePermission.class);
+ }
+
+ public void linkAttribute(long userId, long attributeId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.linkAttribute"))
+ .setLong("userId", userId)
+ .setLong("attributeId", attributeId)
+ .executeUpdate();
+ }
+
+ public void unlinkAttribute(long userId, long attributeId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.unlinkAttribute"))
+ .setLong("userId", userId)
+ .setLong("attributeId", attributeId)
+ .executeUpdate();
+ }
+
+ public Collection<GroupAttribute> getGroupAttributes() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectGroupAttributes"))
+ .executeQuery(GroupAttribute.class);
+ }
+
+ public void linkGroupAttribute(long groupId, long attributeId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.linkGroupAttribute"))
+ .setLong("groupId", groupId)
+ .setLong("attributeId", attributeId)
+ .executeUpdate();
+ }
+
+ public void unlinkGroupAttribute(long groupId, long attributeId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.unlinkGroupAttribute"))
+ .setLong("groupId", groupId)
+ .setLong("attributeId", attributeId)
+ .executeUpdate();
+ }
+
+ public Collection<DeviceAttribute> getDeviceAttributes() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectDeviceAttributes"))
+ .executeQuery(DeviceAttribute.class);
+ }
+
+ public void linkDeviceAttribute(long deviceId, long attributeId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.linkDeviceAttribute"))
+ .setLong("deviceId", deviceId)
+ .setLong("attributeId", attributeId)
+ .executeUpdate();
+ }
+
+ public void unlinkDeviceAttribute(long deviceId, long attributeId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.unlinkDeviceAttribute"))
+ .setLong("deviceId", deviceId)
+ .setLong("attributeId", attributeId)
+ .executeUpdate();
+ }
+
}
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java
index e4bd6f5db..11f147c7c 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/org/traccar/database/PermissionsManager.java
@@ -310,6 +310,18 @@ public class PermissionsManager {
}
}
+ public void checkAttribute(long userId, long attributeId) throws SecurityException {
+ if (!Context.getAttributesManager().checkAttribute(userId, attributeId) && !isAdmin(userId)) {
+ checkManager(userId);
+ for (long managedUserId : getUserPermissions(userId)) {
+ if (Context.getAttributesManager().checkAttribute(managedUserId, attributeId)) {
+ return;
+ }
+ }
+ throw new SecurityException("Attribute access denied");
+ }
+ }
+
public void checkCalendar(long userId, long calendarId) throws SecurityException {
if (!Context.getCalendarManager().checkCalendar(userId, calendarId) && !isAdmin(userId)) {
checkManager(userId);
diff --git a/src/org/traccar/model/Attribute.java b/src/org/traccar/model/Attribute.java
new file mode 100644
index 000000000..9c3b5e43b
--- /dev/null
+++ b/src/org/traccar/model/Attribute.java
@@ -0,0 +1,71 @@
+/*
+ * 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.model;
+
+public class Attribute {
+
+ private long id;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ private String description;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ private String attribute;
+
+ public String getAttribute() {
+ return attribute;
+ }
+
+ public void setAttribute(String attribute) {
+ this.attribute = attribute;
+ }
+
+ private String expression;
+
+ public String getExpression() {
+ return expression;
+ }
+
+ public void setExpression(String expression) {
+ this.expression = expression;
+ }
+
+ private String type;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+}
diff --git a/src/org/traccar/model/AttributePermission.java b/src/org/traccar/model/AttributePermission.java
new file mode 100644
index 000000000..fe2fe7b6e
--- /dev/null
+++ b/src/org/traccar/model/AttributePermission.java
@@ -0,0 +1,41 @@
+/*
+ * 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.model;
+
+public class AttributePermission {
+
+ private long userId;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ private long attributeId;
+
+ public long getAttributeId() {
+ return attributeId;
+ }
+
+ public void setAttributeId(long attributeId) {
+ this.attributeId = attributeId;
+ }
+
+}
diff --git a/src/org/traccar/model/DeviceAttribute.java b/src/org/traccar/model/DeviceAttribute.java
new file mode 100644
index 000000000..e0ac6dd98
--- /dev/null
+++ b/src/org/traccar/model/DeviceAttribute.java
@@ -0,0 +1,40 @@
+/*
+ * 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.model;
+
+public class DeviceAttribute {
+
+ private long deviceId;
+
+ public long getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceId(long deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ private long attributeId;
+
+ public long getAttributeId() {
+ return attributeId;
+ }
+
+ public void setAttributeId(long attributeId) {
+ this.attributeId = attributeId;
+ }
+}
diff --git a/src/org/traccar/model/GroupAttribute.java b/src/org/traccar/model/GroupAttribute.java
new file mode 100644
index 000000000..a7e8a80bc
--- /dev/null
+++ b/src/org/traccar/model/GroupAttribute.java
@@ -0,0 +1,40 @@
+/*
+ * 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.model;
+
+public class GroupAttribute {
+
+ private long groupId;
+
+ public long getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(long groupId) {
+ this.groupId = groupId;
+ }
+
+ private long attributeId;
+
+ public long getAttributeId() {
+ return attributeId;
+ }
+
+ public void setAttributeId(long attributeId) {
+ this.attributeId = attributeId;
+ }
+}
diff --git a/src/org/traccar/processing/ComputedAttributesHandler.java b/src/org/traccar/processing/ComputedAttributesHandler.java
new file mode 100644
index 000000000..ea7c0aa1d
--- /dev/null
+++ b/src/org/traccar/processing/ComputedAttributesHandler.java
@@ -0,0 +1,78 @@
+/*
+ * 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.processing;
+
+import java.util.Collection;
+
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.JexlException;
+import org.apache.commons.jexl2.MapContext;
+import org.traccar.BaseDataHandler;
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Attribute;
+import org.traccar.model.Position;
+
+public class ComputedAttributesHandler extends BaseDataHandler {
+
+ private JexlEngine engine;
+
+ public ComputedAttributesHandler() {
+ engine = new JexlEngine();
+ engine.setStrict(true);
+ }
+
+ public Object computeAttribute(Attribute attribute, Position position) throws JexlException {
+ MapContext expressionContext = new MapContext();
+ expressionContext.set("position", position);
+ return engine.createExpression(attribute.getExpression()).evaluate(expressionContext);
+ }
+
+ @Override
+ protected Position handlePosition(Position position) {
+ Collection<Attribute> attributes = Context.getAttributesManager().getAttributes(
+ Context.getAttributesManager().getAllDeviceAttributes(position.getDeviceId()));
+ for (Attribute attribute : attributes) {
+ if (attribute.getAttribute() != null) {
+ Object result = null;
+ try {
+ result = computeAttribute(attribute, position);
+ } catch (JexlException error) {
+ Log.warning(error);
+ }
+ if (result != null) {
+ try {
+ switch (attribute.getType()) {
+ case "number":
+ position.getAttributes().put(attribute.getAttribute(), (Number) result);
+ break;
+ case "boolean":
+ position.getAttributes().put(attribute.getAttribute(), (Boolean) result);
+ break;
+ default:
+ position.getAttributes().put(attribute.getAttribute(), result.toString());
+ }
+ } catch (ClassCastException error) {
+ Log.warning(error);
+ }
+ }
+ }
+ }
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/CopyAttributesHandler.java b/src/org/traccar/processing/CopyAttributesHandler.java
index 18052d0ea..3a96ca98d 100644
--- a/src/org/traccar/CopyAttributesHandler.java
+++ b/src/org/traccar/processing/CopyAttributesHandler.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ * 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.
@@ -14,8 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.processing;
+import org.traccar.BaseDataHandler;
+import org.traccar.Context;
import org.traccar.model.Position;
public class CopyAttributesHandler extends BaseDataHandler {