diff options
Diffstat (limited to 'src/main/java/org/traccar/handler/ComputedAttributesHandler.java')
-rw-r--r-- | src/main/java/org/traccar/handler/ComputedAttributesHandler.java | 68 |
1 files changed, 54 insertions, 14 deletions
diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java index 620852502..8b010ceae 100644 --- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java +++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java @@ -24,11 +24,15 @@ 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; @@ -39,8 +43,8 @@ import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable @@ -52,15 +56,33 @@ public class ComputedAttributesHandler extends BaseDataHandler { private final JexlEngine engine; + private final JexlFeatures features; + private final boolean includeDeviceAttributes; + private final boolean includeLastAttributes; @Inject public ComputedAttributesHandler(Config config, CacheManager cacheManager) { this.cacheManager = cacheManager; - engine = new JexlEngine(); - engine.setStrict(true); - engine.setFunctions(Collections.singletonMap("math", Math.class)); + 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); + includeLastAttributes = config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LAST_ATTRIBUTES); } private MapContext prepareContext(Position position) { @@ -68,13 +90,17 @@ public class ComputedAttributesHandler extends BaseDataHandler { if (includeDeviceAttributes) { 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)); } } } + Position last = null; + if (includeLastAttributes) { + last = cacheManager.getPosition(position.getDeviceId()); + } 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,9 +108,17 @@ public class ComputedAttributesHandler extends BaseDataHandler { try { if (!method.getReturnType().equals(Map.class)) { result.set(name, method.invoke(position)); + if (last != null) { + result.set(prefixAttribute("last", name), method.invoke(last)); + } } else { - for (Object key : ((Map) method.invoke(position)).keySet()) { - result.set((String) key, ((Map) method.invoke(position)).get(key)); + for (Map.Entry<?, ?> entry : ((Map<?, ?>) method.invoke(position)).entrySet()) { + result.set((String) entry.getKey(), entry.getValue()); + } + if (last != null) { + for (Map.Entry<?, ?> entry : ((Map<?, ?>) method.invoke(last)).entrySet()) { + result.set(prefixAttribute("last", (String) entry.getKey()), entry.getValue()); + } } } } catch (IllegalAccessException | InvocationTargetException error) { @@ -95,12 +129,18 @@ public class ComputedAttributesHandler extends BaseDataHandler { return result; } + private String prefixAttribute(String prefix, String key) { + return prefix + Character.toUpperCase(key.charAt(0)) + key.substring(1); + } + /** * @deprecated logic needs to be extracted to be used in API resource */ @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 |