diff options
Diffstat (limited to 'src/main/java/org/traccar/handler/ComputedAttributesHandler.java')
-rw-r--r-- | src/main/java/org/traccar/handler/ComputedAttributesHandler.java | 114 |
1 files changed, 82 insertions, 32 deletions
diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java index 153da29b9..042747359 100644 --- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java +++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,56 +24,77 @@ import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.List; import io.netty.channel.ChannelHandler; -import org.apache.commons.jexl2.JexlEngine; -import org.apache.commons.jexl2.JexlException; -import org.apache.commons.jexl2.MapContext; +import org.apache.commons.jexl3.JexlFeatures; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.JexlBuilder; +import org.apache.commons.jexl3.introspection.JexlSandbox; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.MapContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseDataHandler; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.AttributesManager; -import org.traccar.database.IdentityManager; import org.traccar.model.Attribute; import org.traccar.model.Device; import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +@Singleton @ChannelHandler.Sharable public class ComputedAttributesHandler extends BaseDataHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ComputedAttributesHandler.class); - private final IdentityManager identityManager; - private final AttributesManager attributesManager; + private final CacheManager cacheManager; private final JexlEngine engine; + private final JexlFeatures features; + private final boolean includeDeviceAttributes; - public ComputedAttributesHandler( - Config config, IdentityManager identityManager, AttributesManager attributesManager) { - this.identityManager = identityManager; - this.attributesManager = attributesManager; - engine = new JexlEngine(); - engine.setStrict(true); - engine.setFunctions(Collections.singletonMap("math", Math.class)); + @Inject + public ComputedAttributesHandler(Config config, CacheManager cacheManager) { + this.cacheManager = cacheManager; + JexlSandbox sandbox = new JexlSandbox(false); + sandbox.allow("com.safe.Functions"); + sandbox.allow(Math.class.getName()); + List.of( + Double.class, Float.class, Integer.class, Long.class, Short.class, + Character.class, Boolean.class, String.class, Byte.class) + .forEach((type) -> sandbox.allow(type.getName())); + features = new JexlFeatures() + .localVar(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LOCAL_VARIABLES)) + .loops(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LOOPS)) + .newInstance(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_NEW_INSTANCE_CREATION)) + .structuredLiteral(true); + engine = new JexlBuilder() + .strict(true) + .namespaces(Collections.singletonMap("math", Math.class)) + .sandbox(sandbox) + .create(); includeDeviceAttributes = config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES); } private MapContext prepareContext(Position position) { MapContext result = new MapContext(); if (includeDeviceAttributes) { - Device device = identityManager.getById(position.getDeviceId()); + Device device = cacheManager.getObject(Device.class, position.getDeviceId()); if (device != null) { - for (Object key : device.getAttributes().keySet()) { - result.set((String) key, device.getAttributes().get(key)); + for (String key : device.getAttributes().keySet()) { + result.set(key, device.getAttributes().get(key)); } } } Set<Method> methods = new HashSet<>(Arrays.asList(position.getClass().getMethods())); - methods.removeAll(Arrays.asList(Object.class.getMethods())); + Arrays.asList(Object.class.getMethods()).forEach(methods::remove); for (Method method : methods) { if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { String name = Character.toLowerCase(method.getName().charAt(3)) + method.getName().substring(4); @@ -82,8 +103,8 @@ public class ComputedAttributesHandler extends BaseDataHandler { if (!method.getReturnType().equals(Map.class)) { result.set(name, method.invoke(position)); } else { - for (Object key : ((Map) method.invoke(position)).keySet()) { - result.set((String) key, ((Map) method.invoke(position)).get(key)); + for (Object key : ((Map<?, ?>) method.invoke(position)).keySet()) { + result.set((String) key, ((Map<?, ?>) method.invoke(position)).get(key)); } } } catch (IllegalAccessException | InvocationTargetException error) { @@ -99,13 +120,14 @@ public class ComputedAttributesHandler extends BaseDataHandler { */ @Deprecated public Object computeAttribute(Attribute attribute, Position position) throws JexlException { - return engine.createExpression(attribute.getExpression()).evaluate(prepareContext(position)); + return engine + .createScript(features, engine.createInfo(), attribute.getExpression()) + .execute(prepareContext(position)); } @Override protected Position handlePosition(Position position) { - Collection<Attribute> attributes = attributesManager.getItems( - attributesManager.getAllDeviceItems(position.getDeviceId())); + Collection<Attribute> attributes = cacheManager.getDeviceObjects(position.getDeviceId(), Attribute.class); for (Attribute attribute : attributes) { if (attribute.getAttribute() != null) { Object result = null; @@ -116,17 +138,45 @@ public class ComputedAttributesHandler extends BaseDataHandler { } if (result != null) { try { - switch (attribute.getType()) { - case "number": - Number numberValue = (Number) result; - position.getAttributes().put(attribute.getAttribute(), numberValue); + switch (attribute.getAttribute()) { + case "valid": + position.setValid((Boolean) result); + break; + case "latitude": + position.setLatitude(((Number) result).doubleValue()); + break; + case "longitude": + position.setLongitude(((Number) result).doubleValue()); + break; + case "altitude": + position.setAltitude(((Number) result).doubleValue()); break; - case "boolean": - Boolean booleanValue = (Boolean) result; - position.getAttributes().put(attribute.getAttribute(), booleanValue); + case "speed": + position.setSpeed(((Number) result).doubleValue()); + break; + case "course": + position.setCourse(((Number) result).doubleValue()); + break; + case "address": + position.setAddress((String) result); + break; + case "accuracy": + position.setAccuracy(((Number) result).doubleValue()); break; default: - position.getAttributes().put(attribute.getAttribute(), result.toString()); + switch (attribute.getType()) { + case "number": + Number numberValue = (Number) result; + position.getAttributes().put(attribute.getAttribute(), numberValue); + break; + case "boolean": + Boolean booleanValue = (Boolean) result; + position.getAttributes().put(attribute.getAttribute(), booleanValue); + break; + default: + position.getAttributes().put(attribute.getAttribute(), result.toString()); + } + break; } } catch (ClassCastException error) { LOGGER.warn("Attribute cast error", error); |