SystemMessage.java

  1. package org.heigit.ors.api.util;

  2. import org.apache.log4j.Logger;
  3. import org.heigit.ors.api.SystemMessageProperties;
  4. import org.heigit.ors.api.requests.isochrones.IsochronesRequest;
  5. import org.heigit.ors.api.requests.matrix.MatrixRequest;
  6. import org.heigit.ors.api.requests.routing.RouteRequest;
  7. import org.heigit.ors.matrix.MatrixMetricsType;
  8. import org.heigit.ors.routing.RoutingProfileType;
  9. import org.heigit.ors.routing.RoutingRequest;
  10. import org.heigit.ors.routing.WeightingMethod;

  11. import java.sql.Timestamp;
  12. import java.time.Instant;
  13. import java.util.*;

  14. public class SystemMessage {
  15.     private static final Logger LOGGER = Logger.getLogger(SystemMessage.class.getName());
  16.     private static List<Message> messages;

  17.     private SystemMessage() {
  18.     }

  19.     public static String getSystemMessage(Object requestObj, SystemMessageProperties messageProperties) {
  20.         if (messages == null) {
  21.             loadMessages(messageProperties);
  22.         }
  23.         if (messages.isEmpty()) {
  24.             return "";
  25.         }
  26.         if (requestObj == null) {
  27.             requestObj = "";
  28.         }
  29.         RequestParams params = new RequestParams();
  30.         // V1
  31.         if (requestObj.getClass() == RoutingRequest.class) {
  32.             extractParams((RoutingRequest) requestObj, params);
  33.         } else if (requestObj.getClass() == org.heigit.ors.matrix.MatrixRequest.class) {
  34.             extractParams((org.heigit.ors.matrix.MatrixRequest) requestObj, params);
  35.         } else if (requestObj.getClass() == org.heigit.ors.isochrones.IsochroneRequest.class) {
  36.             extractParams((org.heigit.ors.isochrones.IsochroneRequest) requestObj, params);
  37.         }
  38.         // V2
  39.         else if (requestObj.getClass() == RouteRequest.class) {
  40.             extractParams((RouteRequest) requestObj, params);
  41.         } else if (requestObj.getClass() == MatrixRequest.class) {
  42.             extractParams((MatrixRequest) requestObj, params);
  43.         } else if (requestObj.getClass() == IsochronesRequest.class) {
  44.             extractParams((IsochronesRequest) requestObj, params);
  45.         }
  46.         return selectMessage(params);
  47.     }

  48.     private static void extractParams(RoutingRequest req, RequestParams params) {
  49.         params.setApiVersion("1");
  50.         params.setApiFormat(req.getResponseFormat());
  51.         params.setRequestService("routing");
  52.         params.setRequestProfiles(RoutingProfileType.getName(req.getSearchParameters().getProfileType()));
  53.         params.setRequestPreferences(WeightingMethod.getName(req.getSearchParameters().getWeightingMethod()));
  54.     }

  55.     private static void extractParams(org.heigit.ors.matrix.MatrixRequest req, RequestParams params) {
  56.         params.setApiVersion("1");
  57.         params.setApiFormat("json");
  58.         params.setRequestService("matrix");
  59.         params.setRequestProfiles(RoutingProfileType.getName(req.getProfileType()));
  60.         params.setRequestPreference(MatrixMetricsType.getMetricsNamesFromInt(req.getMetrics()));
  61.     }

  62.     private static void extractParams(org.heigit.ors.isochrones.IsochroneRequest req, RequestParams params) {
  63.         params.setApiVersion("1");
  64.         params.setApiFormat("geojson");
  65.         params.setRequestService("isochrones");
  66.         params.setRequestProfile(req.getProfilesForAllTravellers());
  67.         params.setRequestPreference(req.getWeightingsForAllTravellers());
  68.     }

  69.     private static void extractParams(RouteRequest req, RequestParams params) {
  70.         params.setApiVersion("2");
  71.         params.setApiFormat(req.getResponseType().toString());
  72.         params.setRequestService("routing");
  73.         params.setRequestProfiles(req.getProfile().toString());
  74.         params.setRequestPreferences(req.hasRoutePreference() ? req.getRoutePreference().toString() : "");
  75.     }

  76.     private static void extractParams(MatrixRequest req, RequestParams params) {
  77.         params.setApiVersion("2");
  78.         params.setApiFormat("json");
  79.         params.setRequestService("matrix");
  80.         params.setRequestProfiles(req.getProfile() != null ? req.getProfile().toString() : "driving-car");
  81.         params.setRequestPreference(req.getMetricsStrings().isEmpty() ? new HashSet<>(List.of("duration")) : req.getMetricsStrings());
  82.     }

  83.     private static void extractParams(IsochronesRequest req, RequestParams params) {
  84.         params.setApiVersion("2");
  85.         params.setApiFormat("geojson");
  86.         params.setRequestService("isochrones");
  87.         params.setRequestProfiles(req.getProfile() != null ? req.getProfile().toString() : "driving-car");
  88.         params.setRequestPreferences(req.hasRangeType() ? req.getRangeType().name() : "TIME");
  89.     }

  90.     private static String selectMessage(RequestParams params) {
  91.         for (Message message : messages) {
  92.             if (message.applicableForRequest(params)) {
  93.                 return message.getText();
  94.             }
  95.         }
  96.         return "";
  97.     }

  98.     private static void loadMessages(SystemMessageProperties messageProperties) {
  99.         messages = new ArrayList<>();

  100.         for (SystemMessageProperties.MessageObject message : messageProperties.getMessages()) {
  101.             try {
  102.                 if (message.isActive()) {
  103.                     List<SystemMessage.Condition> conditions = new ArrayList<>();
  104.                     loadConditionsForMessage(message, conditions);
  105.                     messages.add(new SystemMessage.Message(message.getText(), conditions));
  106.                 }
  107.             } catch (Exception e) {
  108.                 // ignore otherwise incomplete messages entirely
  109.                 LOGGER.warn("Invalid SystemMessage object in ors config %s.".formatted(message.toString().substring(18)));
  110.             }
  111.         }
  112.         AppConfigMigration.loadSystemMessagesfromAppConfig(messages);
  113.         if (!messages.isEmpty())
  114.             LOGGER.info("SystemMessage loaded %s messages.".formatted(messages.size()));
  115.     }

  116.     private static void loadConditionsForMessage(SystemMessageProperties.MessageObject message, List<Condition> conditions) {
  117.         try {
  118.             for (Map<String, String> conditionMap : message.getCondition()) {
  119.                 for (Map.Entry<String, String> condition : conditionMap.entrySet()) {
  120.                     conditions.add(new Condition(condition.getKey(), condition.getValue()));
  121.                 }
  122.             }
  123.         } catch (Exception e) {
  124.             // ignore missing condition block and keep message
  125.             LOGGER.info("Invalid or missing condition in message object.");
  126.         }
  127.     }

  128.     static class Message {
  129.         private final String text;
  130.         private final List<Condition> conditions;

  131.         public Message(String text, List<Condition> conditions) {
  132.             this.text = text;
  133.             this.conditions = conditions;
  134.         }

  135.         public String getText() {
  136.             return text;
  137.         }

  138.         public boolean applicableForRequest(RequestParams params) {
  139.             for (Condition condition : conditions) {
  140.                 if (!condition.fulfilledBy(params)) {
  141.                     return false;
  142.                 }
  143.             }
  144.             return true;
  145.         }
  146.     }

  147.     static class Condition {
  148.         private final String type;
  149.         private final String[] values;

  150.         public Condition(String type, String valuesCSV) {
  151.             this.type = type;
  152.             this.values = valuesCSV.split(",");
  153.         }

  154.         public boolean fulfilledBy(RequestParams params) {
  155.             switch (type) {
  156.                 case "time_before" -> {
  157.                     return new Timestamp(System.currentTimeMillis()).getTime() < Date.from(Instant.parse(this.values[0])).getTime();
  158.                 }
  159.                 case "time_after" -> {
  160.                     return new Timestamp(System.currentTimeMillis()).getTime() > Date.from(Instant.parse(this.values[0])).getTime();
  161.                 }
  162.                 case "api_version" -> {
  163.                     return matchApiVersion(params);
  164.                 }
  165.                 case "api_format" -> {
  166.                     return matchApiFormat(params);
  167.                 }
  168.                 case "request_service" -> {
  169.                     return matchRequestService(params);
  170.                 }
  171.                 case "request_profile" -> {
  172.                     return matchRequestProfiles(params);
  173.                 }
  174.                 case "request_preference" -> {
  175.                     return matchRequestPreferences(params);
  176.                 }
  177.                 default -> // unknown rule
  178.                         LOGGER.warn("Invalid condition set in system_message.");
  179.             }
  180.             return false;
  181.         }

  182.         private boolean matchApiVersion(RequestParams params) {
  183.             for (String val : this.values) {
  184.                 if (params.getApiVersion().equalsIgnoreCase(val)) return true;
  185.             }
  186.             return false;
  187.         }

  188.         private boolean matchApiFormat(RequestParams params) {
  189.             for (String val : this.values) {
  190.                 if (params.getApiFormat().equalsIgnoreCase(val)) return true;
  191.             }
  192.             return false;
  193.         }

  194.         private boolean matchRequestService(RequestParams params) {
  195.             for (String val : this.values) {
  196.                 if (params.getRequestService().equalsIgnoreCase(val)) return true;
  197.             }
  198.             return false;
  199.         }

  200.         private boolean matchRequestProfiles(RequestParams params) {
  201.             for (String val : this.values) {
  202.                 for (String param : params.getRequestProfiles()) {
  203.                     if (param.equalsIgnoreCase(val)) return true;
  204.                 }
  205.             }
  206.             return false;
  207.         }

  208.         private boolean matchRequestPreferences(RequestParams params) {
  209.             for (String val : this.values) {
  210.                 for (String param : params.getRequestPreferences()) {
  211.                     if (param.equalsIgnoreCase(val)) return true;
  212.                 }
  213.             }
  214.             return false;
  215.         }
  216.     }

  217.     private static class RequestParams {
  218.         private String apiVersion = "";
  219.         private String apiFormat = "";
  220.         private String requestService = "";
  221.         private final Set<String> requestProfiles = new HashSet<>();
  222.         private final Set<String> requestPreferences = new HashSet<>();

  223.         public String getApiVersion() {
  224.             return apiVersion;
  225.         }

  226.         public void setApiVersion(String apiVersion) {
  227.             this.apiVersion = apiVersion;
  228.         }

  229.         public String getApiFormat() {
  230.             return apiFormat;
  231.         }

  232.         public void setApiFormat(String apiFormat) {
  233.             this.apiFormat = apiFormat;
  234.         }

  235.         public String getRequestService() {
  236.             return requestService;
  237.         }

  238.         public void setRequestService(String requestService) {
  239.             this.requestService = requestService;
  240.         }

  241.         public Set<String> getRequestProfiles() {
  242.             return requestProfiles;
  243.         }

  244.         public void setRequestProfiles(String requestProfiles) {
  245.             this.requestProfiles.add(requestProfiles);
  246.         }

  247.         public void setRequestProfile(Set<String> requestProfile) {
  248.             this.requestProfiles.addAll(requestProfile);
  249.         }

  250.         public Set<String> getRequestPreferences() {
  251.             return requestPreferences;
  252.         }

  253.         public void setRequestPreferences(String requestPreferences) {
  254.             this.requestPreferences.add(requestPreferences);
  255.         }

  256.         public void setRequestPreference(Set<String> requestPreference) {
  257.             this.requestPreferences.addAll(requestPreference);
  258.         }
  259.     }
  260. }