/******************************************************************************* * Copyright 2013-2016 alladin-IT GmbH * * 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 at.alladin.rmbt.controlServer; import java.sql.Connection; import java.sql.SQLException; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.Format; import java.text.MessageFormat; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.TimeZone; import java.util.UUID; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.restlet.resource.Get; import org.restlet.resource.Post; import at.alladin.rmbt.db.Client; import at.alladin.rmbt.db.Test; import at.alladin.rmbt.db.TestNdt; import at.alladin.rmbt.db.fields.Field; import at.alladin.rmbt.db.fields.TimestampField; import at.alladin.rmbt.shared.Helperfunctions; import at.alladin.rmbt.shared.ResourceManager; import at.alladin.rmbt.shared.SignificantFormat; public class TestResultDetailResource extends ServerResource { private JSONObject addObject(final JSONArray array, final String key) throws JSONException { final JSONObject newObject = new JSONObject(); newObject.put("title", getKeyTranslation(key)); array.put(newObject); return newObject; } private void addString(final JSONArray array, final String title, final String value) throws JSONException { if (value != null && !value.isEmpty()) addObject(array, title).put("value", value); } private void addString(final JSONArray array, final String title, final Field field) throws JSONException { if (!field.isNull()) addString(array, title, field.toString()); } private void addInt(final JSONArray array, final String title, final Field field) throws JSONException { if (!field.isNull()) addObject(array, title).put("value", field.intValue()); } private String getKeyTranslation(final String key) { try { return labels.getString("key_" + key); } catch (final MissingResourceException e) { return key; } } @Post("json") public String request(final String entity) { long startTime = System.currentTimeMillis(); addAllowOrigin(); JSONObject request = null; final ErrorList errorList = new ErrorList(); final JSONObject answer = new JSONObject(); String answerString; final String clientIpRaw = getIP(); System.out.println(MessageFormat.format(labels.getString("NEW_TESTRESULT_DETAIL"), clientIpRaw)); if (entity != null && !entity.isEmpty()) // try parse the string to a JSON object try { request = new JSONObject(entity); String lang = request.optString("language"); // Load Language Files for Client final List<String> langs = Arrays.asList(settings.getString("RMBT_SUPPORTED_LANGUAGES").split(",\\s*")); if (langs.contains(lang)) { errorList.setLanguage(lang); labels = ResourceManager.getSysMsgBundle(new Locale(lang)); } else lang = settings.getString("RMBT_DEFAULT_LANGUAGE"); // System.out.println(request.toString(4)); if (conn != null) { final Client client = new Client(conn); final Test test = new Test(conn); TestNdt ndt = new TestNdt(conn); final String testUuid = request.optString("test_uuid"); if (testUuid != null && test.getTestByUuid(UUID.fromString(testUuid)) > 0 && client.getClientByUid(test.getField("client_id").intValue())) { if (!ndt.loadByTestId(test.getUid())) ndt = null; final Locale locale = new Locale(lang); final Format format = new SignificantFormat(2, locale); final JSONArray resultList = new JSONArray(); boolean dualSim = false; final Field dualSimField = test.getField("dual_sim"); if (! dualSimField.isNull() && (dualSimField.toString() == "true")) dualSim = true; final JSONObject singleItem = addObject(resultList, "time"); final Field timeField = test.getField("time"); if (!timeField.isNull()) { final Date date = ((TimestampField) timeField).getDate(); final long time = date.getTime(); singleItem.put("time", time); //csv 3 final Field timezoneField = test.getField("timezone"); if (!timezoneField.isNull()) { final String tzString = timezoneField.toString(); final TimeZone tz = TimeZone.getTimeZone(timezoneField.toString()); singleItem.put("timezone", tzString); final DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale); dateFormat.setTimeZone(tz); singleItem.put("value", dateFormat.format(date)); final Format tzFormat = new DecimalFormat("+0.##;-0.##", new DecimalFormatSymbols(locale)); final float offset = tz.getOffset(time) / 1000f / 60f / 60f; addString(resultList, "timezone", String.format("UTC%sh", tzFormat.format(offset))); } } // speed download in Mbit/s (converted from kbit/s) - csv 10 (in kbit/s) final Field downloadField = test.getField("speed_download"); if (!downloadField.isNull()) { final String download = format.format(downloadField.doubleValue() / 1000d); addString(resultList, "speed_download", String.format("%s %s", download, labels.getString("RESULT_DOWNLOAD_UNIT"))); } // speed upload im MBit/s (converted from kbit/s) - csv 11 (in kbit/s) final Field uploadField = test.getField("speed_upload"); if (!uploadField.isNull()) { final String upload = format.format(uploadField.doubleValue() / 1000d); addString(resultList, "speed_upload", String.format("%s %s", upload, labels.getString("RESULT_UPLOAD_UNIT"))); } // median ping in ms final Field pingMedianField = test.getField("ping_median"); if (!pingMedianField.isNull()) { final String pingMedian = format.format(pingMedianField.doubleValue() / 1000000d); addString(resultList, "ping_median", String.format("%s %s", pingMedian, labels.getString("RESULT_PING_UNIT"))); } // signal strength RSSI in dBm - csv 13 final Field signalStrengthField = test.getField("signal_strength"); if (!dualSim && !signalStrengthField.isNull()) addString( resultList, "signal_strength", String.format("%d %s", signalStrengthField.intValue(), labels.getString("RESULT_SIGNAL_UNIT"))); //signal strength RSRP in dBm (LTE) - csv 29 final Field lteRsrpField = test.getField("lte_rsrp"); if (!dualSim && !lteRsrpField.isNull()) addString( resultList, "signal_rsrp", String.format("%d %s", lteRsrpField.intValue(), labels.getString("RESULT_SIGNAL_UNIT"))); //signal quality in LTE, RSRQ in dB final Field lteRsrqField = test.getField("lte_rsrq"); if (!dualSim && !lteRsrqField.isNull()) addString( resultList, "signal_rsrq", String.format("%d %s", lteRsrqField.intValue(), labels.getString("RESULT_DB_UNIT"))); // network, eg. "3G (HSPA+) //TODO fix helper-function final Field networkTypeField = test.getField("network_type"); if (!dualSim && !networkTypeField.isNull()) addString(resultList, "network_type", Helperfunctions.getNetworkTypeName(networkTypeField.intValue())); // geo-location JSONObject locationJson = getGeoLocation(test, settings, conn, labels); if (locationJson != null) { if (locationJson.has("location")) { addString(resultList, "location", locationJson.getString("location")); } if (locationJson.has("country_location")) { addString(resultList, "country_location", locationJson.getString("country_location")); } if (locationJson.has("motion")) { addString(resultList, "motion", locationJson.getString("motion")); } } // country derived from AS registry final Field countryAsnField = test.getField("country_asn"); if (!countryAsnField.isNull()) addString(resultList, "country_asn", countryAsnField.toString()); // country derived from geo-IP database final Field countryGeoipField = test.getField("country_geoip"); if (!countryGeoipField.isNull()) addString(resultList, "country_geoip", countryGeoipField.toString()); final Field zipCodeField = test.getField("zip_code"); if (!zipCodeField.isNull()) { final String zipCode = zipCodeField.toString(); final int zipCodeInt = zipCodeField.intValue(); if (zipCodeInt > 999 || zipCodeInt <= 9999) // plausibility of zip code (must be 4 digits in Austria) addString(resultList, "zip_code", zipCode); } /* final Field gkzField = test.getField("gkz"); if (!gkzField.isNull()) { addString(resultList, "gkz", gkzField.toString()); } */ final Field communityField = test.getField("community"); if (!communityField.isNull()) { addString(resultList, "community", communityField.toString()); } final Field cov800catField = test.getField("cov800cat"); if (!cov800catField.isNull()) { addString(resultList, "cov800cat", cov800catField.toString()); } final Field districtField = test.getField("district"); if (!districtField.isNull()) { addString(resultList, "district", districtField.toString()); } final Field provinceField = test.getField("province"); if (!provinceField.isNull()) { addString(resultList, "province", provinceField.toString()); } // public client ip (private) addString(resultList, "client_public_ip", test.getField("client_public_ip")); // AS number - csv 24 addString(resultList, "client_public_ip_as", test.getField("public_ip_asn")); // name of AS addString(resultList, "client_public_ip_as_name", test.getField("public_ip_as_name")); // reverse hostname (from ip) - (private) addString(resultList, "client_public_ip_rdns", test.getField("public_ip_rdns")); // operator - derived from provider_id (only for pre-defined operators) //TODO replace provider-information by more generic information addString(resultList, "provider", test.getField("provider_id_name")); // type of client local ip (private) addString(resultList, "client_local_ip", test.getField("client_ip_local_type")); // nat-translation of client - csv 23 addString(resultList, "nat_type", test.getField("nat_type")); // wifi base station id SSID (numberic) eg 01:2c:3d.. addString(resultList, "wifi_ssid", test.getField("wifi_ssid")); // wifi base station id - BSSID (text) eg 'my hotspot' addString(resultList, "wifi_bssid", test.getField("wifi_bssid")); // nominal link speed of wifi connection in MBit/s final Field linkSpeedField = test.getField("wifi_link_speed"); if (!linkSpeedField.isNull()) addString( resultList, "wifi_link_speed", String.format("%s %s", linkSpeedField.toString(), labels.getString("RESULT_WIFI_LINK_SPEED_UNIT"))); // name of mobile network operator (eg. 'T-Mobile AT') if (!dualSim) addString(resultList, "network_operator_name", test.getField("network_operator_name")); // mobile network name derived from MCC/MNC of network, eg. '232-01' final Field networkOperatorField = test.getField("network_operator"); if (!dualSim) { // mobile provider name, eg. 'Hutchison Drei' (derived from mobile_provider_id) final Field mobileProviderNameField = test.getField("mobile_provider_name"); if (mobileProviderNameField.isNull()) // eg. '248-02' addString(resultList, "network_operator", networkOperatorField); else { if (networkOperatorField.isNull()) addString(resultList, "network_operator", mobileProviderNameField); else // eg. 'Hutchison Drei (232-10)' addString(resultList, "network_operator", String.format("%s (%s)", mobileProviderNameField, networkOperatorField)); } addString(resultList, "network_sim_operator_name", test.getField("network_sim_operator_name")); final Field networkSimOperatorField = test.getField("network_sim_operator"); final Field networkSimOperatorTextField = test.getField("network_sim_operator_mcc_mnc_text"); if (networkSimOperatorTextField.isNull()) addString(resultList, "network_sim_operator", networkSimOperatorField); else addString(resultList, "network_sim_operator", String.format("%s (%s)", networkSimOperatorTextField, networkSimOperatorField)); final Field roamingTypeField = test.getField("roaming_type"); if (!roamingTypeField.isNull()) addString(resultList, "roaming", Helperfunctions.getRoamingType(labels, roamingTypeField.intValue())); } //dualSim final long totalDownload = test.getField("total_bytes_download").longValue(); final long totalUpload = test.getField("total_bytes_upload").longValue(); final long totalBytes = totalDownload + totalUpload; if (totalBytes > 0) { final String totalBytesString = format.format(totalBytes / (1000d * 1000d)); addString( resultList, "total_bytes", String.format("%s %s", totalBytesString, labels.getString("RESULT_TOTAL_BYTES_UNIT"))); } // interface volumes - total including control-server and pre-tests (and other tests) final long totalIfDownload = test.getField("test_if_bytes_download").longValue(); final long totalIfUpload = test.getField("test_if_bytes_upload").longValue(); // output only total of down- and upload final long totalIfBytes = totalIfDownload + totalIfUpload; if (totalIfBytes > 0) { final String totalIfBytesString = format.format(totalIfBytes / (1000d * 1000d)); addString( resultList, "total_if_bytes", String.format("%s %s", totalIfBytesString, labels.getString("RESULT_TOTAL_BYTES_UNIT"))); } // interface volumes during test // download test - volume in download direction final long testDlIfBytesDownload = test.getField("testdl_if_bytes_download").longValue(); if (testDlIfBytesDownload > 0l) { final String testDlIfBytesDownloadString = format.format(testDlIfBytesDownload / (1000d * 1000d)); addString( resultList, "testdl_if_bytes_download", String.format("%s %s", testDlIfBytesDownloadString, labels.getString("RESULT_TOTAL_BYTES_UNIT"))); } // download test - volume in upload direction final long testDlIfBytesUpload = test.getField("testdl_if_bytes_upload").longValue(); if (testDlIfBytesUpload > 0l) { final String testDlIfBytesUploadString = format.format(testDlIfBytesUpload / (1000d * 1000d)); addString( resultList, "testdl_if_bytes_upload", String.format("%s %s", testDlIfBytesUploadString, labels.getString("RESULT_TOTAL_BYTES_UNIT"))); } // upload test - volume in upload direction final long testUlIfBytesUpload = test.getField("testul_if_bytes_upload").longValue(); if (testUlIfBytesUpload > 0l) { final String testUlIfBytesUploadString = format.format(testUlIfBytesUpload / (1000d * 1000d)); addString( resultList, "testul_if_bytes_upload", String.format("%s %s", testUlIfBytesUploadString, labels.getString("RESULT_TOTAL_BYTES_UNIT"))); } // upload test - volume in download direction final long testUlIfBytesDownload = test.getField("testul_if_bytes_download").longValue(); if (testDlIfBytesDownload > 0l) { final String testUlIfBytesDownloadString = format.format(testUlIfBytesDownload / (1000d * 1000d)); addString( resultList, "testul_if_bytes_download", String.format("%s %s", testUlIfBytesDownloadString, labels.getString("RESULT_TOTAL_BYTES_UNIT"))); } //start time download-test final Field time_dl_ns = test.getField("time_dl_ns"); if (!time_dl_ns.isNull()) { addString(resultList,"time_dl", String.format("%s %s", format.format(time_dl_ns.doubleValue() / 1000000000d), //convert ns to s labels.getString("RESULT_DURATION_UNIT"))); } //duration download-test final Field duration_download_ns = test.getField("nsec_download"); if (!duration_download_ns.isNull()) { addString(resultList,"duration_dl", String.format("%s %s", format.format(duration_download_ns.doubleValue() / 1000000000d), //convert ns to s labels.getString("RESULT_DURATION_UNIT"))); } //start time upload-test final Field time_ul_ns = test.getField("time_ul_ns"); if (!time_ul_ns.isNull()) { addString(resultList,"time_ul", String.format("%s %s", format.format(time_ul_ns.doubleValue() / 1000000000d), //convert ns to s labels.getString("RESULT_DURATION_UNIT"))); } //duration upload-test final Field duration_upload_ns = test.getField("nsec_upload"); if (!duration_upload_ns.isNull()) { addString(resultList,"duration_ul", String.format("%s %s", format.format(duration_upload_ns.doubleValue() / 1000000000d), //convert ns to s labels.getString("RESULT_DURATION_UNIT"))); } if (ndt != null) { final String downloadNdt = format.format(ndt.getField("s2cspd").doubleValue()); addString(resultList, "speed_download_ndt", String.format("%s %s", downloadNdt, labels.getString("RESULT_DOWNLOAD_UNIT"))); final String uploaddNdt = format.format(ndt.getField("c2sspd").doubleValue()); addString(resultList, "speed_upload_ndt", String.format("%s %s", uploaddNdt, labels.getString("RESULT_UPLOAD_UNIT"))); // final String pingNdt = // format.format(ndt.getField("avgrtt").doubleValue()); // addString(resultList, "ping_ndt", // String.format("%s %s", pingNdt, // labels.getString("RESULT_PING_UNIT"))); } addString(resultList, "server_name", test.getField("server_name")); addString(resultList, "plattform", test.getField("plattform")); addString(resultList, "os_version", test.getField("os_version")); addString(resultList, "model", test.getField("model_fullname")); addString(resultList, "client_name", test.getField("client_name")); addString(resultList, "client_software_version", test.getField("client_software_version")); final String encryption = test.getField("encryption").toString(); if (encryption != null) { addString( resultList, "encryption", "NONE".equals(encryption) ? labels .getString("key_encryption_false") : labels.getString("key_encryption_true")); } addString(resultList, "client_version", test.getField("client_version")); addString( resultList, "duration", String.format("%d %s", test.getField("duration").intValue(), labels.getString("RESULT_DURATION_UNIT"))); // number of threads for download-test final Field num_threads = test.getField("num_threads"); if (!num_threads.isNull()) { addInt(resultList,"num_threads",num_threads); } //number of threads for upload-test final Field num_threads_ul = test.getField("num_threads_ul"); if (!num_threads_ul.isNull()) { addInt(resultList,"num_threads_ul",num_threads_ul); } //dz 2013-11-09 removed UUID from details as users might get confused by two // ids; //addString(resultList, "uuid", String.format("T%s", test.getField("uuid"))); final Field openTestUUIDField = test.getField("open_test_uuid"); if (! openTestUUIDField.isNull()) addString(resultList, "open_test_uuid", String.format("O%s", openTestUUIDField)); final Field openUUIDField = test.getField("open_uuid"); if (! openUUIDField.isNull()) addString(resultList, "open_uuid", String.format("P%s", openUUIDField)); //todo: Add "user_server_selection" Flag //number of threads for upload-test final Field tag = test.getField("tag"); if (!tag.isNull()) { addString(resultList,"tag",tag); } if (ndt != null) { addString(resultList, "ndt_details_main", ndt.getField("main")); addString(resultList, "ndt_details_stat", ndt.getField("stat")); addString(resultList, "ndt_details_diag", ndt.getField("diag")); } if (resultList.length() == 0) errorList.addError("ERROR_DB_GET_TESTRESULT_DETAIL"); answer.put("testresultdetail", resultList); } else errorList.addError("ERROR_REQUEST_TEST_RESULT_DETAIL_NO_UUID"); } else errorList.addError("ERROR_DB_CONNECTION"); } catch (final JSONException e) { errorList.addError("ERROR_REQUEST_JSON"); System.out.println("Error parsing JSDON Data " + e.toString()); } else errorList.addErrorString("Expected request is missing."); try { answer.putOpt("error", errorList.getList()); } catch (final JSONException e) { System.out.println("Error saving ErrorList: " + e.toString()); } answerString = answer.toString(); long elapsedTime = System.currentTimeMillis() - startTime; System.out.println(MessageFormat.format(labels.getString("NEW_TESTRESULT_DETAIL_SUCCESS"), clientIpRaw, Long.toString(elapsedTime))); return answerString; } @Get("json") public String retrieve(final String entity) { return request(entity); } /** * * @param test * @param settings * @param conn * @return * @throws JSONException */ public static JSONObject getGeoLocation(Test test, ResourceBundle settings, Connection conn, ResourceBundle labels) throws JSONException { JSONObject json = new JSONObject(); // geo-location final Field latField = test.getField("geo_lat"); //csv 6 final Field longField = test.getField("geo_long"); //csv 7 final Field accuracyField = test.getField("geo_accuracy"); final Field providerField = test.getField("geo_provider"); //csv 8 if (!(latField.isNull() || longField.isNull() || accuracyField.isNull())) { final double accuracy = accuracyField.doubleValue(); if (accuracy < Double.parseDouble(settings.getString("RMBT_GEO_ACCURACY_DETAIL_LIMIT"))) { final StringBuilder geoString = new StringBuilder(Helperfunctions.geoToString(latField.doubleValue(), longField.doubleValue())); geoString.append(" ("); if (! providerField.isNull()) { String provider = providerField.toString().toUpperCase(Locale.US); switch(provider) { case "NETWORK": provider = labels.getString("key_geo_source_network"); break; case "GPS": provider = labels.getString("key_geo_source_gps"); break; } geoString.append(provider); geoString.append(", "); } geoString.append(String.format(Locale.US, "+/- %.0f m", accuracy)); geoString.append(")"); json.put("location", geoString.toString()); //also try getting the distance during the test final Date clientDate = ((TimestampField) test.getField("client_time")).getDate(); final long clientTime = clientDate.getTime(); try { OpenTestResource.LocationGraph locGraph = new OpenTestResource.LocationGraph(test.getUid(), clientTime, conn); if ((locGraph.getTotalDistance() > 0) && locGraph.getTotalDistance() <= Double.parseDouble(settings.getString("RMBT_GEO_DISTANCE_DETAIL_LIMIT"))) { json.put("motion", Math.round(locGraph.getTotalDistance()) + " m"); } } catch (SQLException ex) { //cannot happen since the test uid has to exist in here ex.printStackTrace(); } } // country derived from location final Field countryLocationField = test.getField("country_location"); if (!countryLocationField.isNull()) { json.put("country_location", countryLocationField.toString()); } } return json; } }