diff --git a/LICENSE.txt b/LICENSE.txt index d12e4ac..cbbb5e1 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Co-Sky project +Copyright (c) 2014 NFleet Oy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/fi/cosky/sdk/API.java b/fi/cosky/sdk/API.java index 5f2896d..0b68f9f 100644 --- a/fi/cosky/sdk/API.java +++ b/fi/cosky/sdk/API.java @@ -13,6 +13,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import com.google.gson.*; @@ -24,8 +25,8 @@ */ /** - * API-class to handle the communication between SDK-user and NFleet - * optimization's REST-API. + * API class to handle the communication between SDK user and NFleet + * optimization's REST API. */ public class API { private String baseUrl; @@ -35,11 +36,12 @@ public class API { private TokenData tokenData; private boolean timed; private ObjectCache objectCache; - private boolean retry; private boolean useMimeTypes; private MimeTypeHelper helper; - private static int RETRY_WAIT_TIME = 2000; + private static int RETRY_WAIT_TIME_FACTOR = 1000; + private static int UNAVAILABLE_RETRY_WAIT_TIME_FACTOR = 10000; + private static int REQUEST_ATTEMPTS = 3; static Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'").create(); @@ -47,37 +49,33 @@ public API(String baseUrl) { this.baseUrl = baseUrl; this.objectCache = new ObjectCache(); this.timed = false; - this.retry = true; - this.useMimeTypes = true; //change this when production will support mimetypes. + this.useMimeTypes = true; this.helper = new MimeTypeHelper(); - - //Delete-Verb causes connection to keep something somewhere that causes the next request to fail. - //this hopefully helps with that. - System.setProperty("http.keepAlive", "false"); } private boolean authenticate() { return authenticate(this.ClientKey, this.ClientSecret); - } - + } public boolean authenticate(String username, String password) { this.ClientKey = username; this.ClientSecret = password; - System.out.println("Authenticating API with username: " +username + " and pass: " + password); + System.out.println("Authenticating API user " + username); try { ResponseData result = navigate(ResponseData.class, getAuthLink()); if (result == null || result.getItems() != null) { - System.out.println("Could not authenticate, please check credentials and service status from http://status.nfleet.fi"); + System.out.println("Unexpected authentication response. Please check credentials and service status."); return false; } TokenData authenticationData = navigate(TokenData.class, result.getLocation()); this.tokenData = authenticationData; + } catch (NFleetRequestException e) { + return false; } catch (Exception e) { - System.out.println( e.toString()); + System.out.println("Authentication failed: " + e.toString()); return false; } return true; @@ -95,59 +93,9 @@ public T navigate(Class tClass, Link l) throws IOExcepti return navigate(tClass, l, null); } - @SuppressWarnings("unchecked") - public T navigate(Class tClass, Link l, HashMap queryParameters) throws IOException { - Object result; - retry = true; - long start = 0; - long end; - - if (isTimed()) { - start = System.currentTimeMillis(); - } - - if (tClass.equals(TokenData.class)) { - result = sendRequest(l, tClass, null); - return (T) result; - } - - if (l.getRel().equals("authenticate")) { - HashMap headers = new HashMap(); - String authorization = "Basic " + Base64.encodeBase64String((this.ClientKey + ":" + this.ClientSecret).getBytes()); - headers.put("authorization", authorization); - result = sendRequestWithAddedHeaders(Verb.POST, this.baseUrl + l.getUri(), tClass, null, headers); - return (T) result; - } - - String uri = l.getUri(); - if (l.getMethod().equals("GET") && queryParameters != null && !queryParameters.isEmpty()) { - StringBuilder sb = new StringBuilder(uri + "?"); - - for (String key : queryParameters.keySet()) { - sb.append(key + "=" + queryParameters.get(key) + "&"); - } - sb.deleteCharAt(sb.length() - 1); - uri = sb.toString(); - } - - if (l.getMethod().equals("GET") && !uri.contains(":")) { - result = sendRequest(l, tClass, null); - } else if (l.getMethod().equals("PUT")) { - result = sendRequest(l, tClass, null); - } else if (l.getMethod().equals("POST")) { - result = sendRequest(l, tClass, null); - } else if (l.getMethod().equals("DELETE")) { - result = sendRequest(l, tClass, new Object()); - } else { - result = sendRequest(l, tClass, null); - } - if (isTimed()) { - end = System.currentTimeMillis(); - long time = end - start; - System.out.println("Method " + l.getMethod() + " on " + l.getUri() + " doing " + l.getRel() + " took " + time + " ms."); - } - return (T) result; - } + public T navigate(Class tClass, Link l, Object object) throws IOException { + return navigate(tClass, l, object, null); + } /** * Navigate method for sending data @@ -159,21 +107,36 @@ public T navigate(Class tClass, Link l, HashMap T navigate(Class tClass, Link l, Object object) throws IOException { - long start = 0; + public T navigate(Class tClass, Link l, Object object, HashMap queryParameters) throws IOException { + long start = 0; long end; - - if (isTimed()) { - start = System.currentTimeMillis(); - } - - Object result = sendRequest(l, tClass, object); + + if (l.getMethod().equals("GET") && queryParameters != null && !queryParameters.isEmpty()) { + String uri = l.getUri(); + StringBuilder sb = new StringBuilder(uri + "?"); + + for (String key : queryParameters.keySet()) { + sb.append(key + "=" + queryParameters.get(key) + "&"); + } + sb.deleteCharAt(sb.length() - 1); + l.setUri(sb.toString()); + } + + if (l.getMethod().equals(Verb.DELETE)) object = new Object(); + + if (isTimed()) { + System.out.println("Doing " + l.getMethod() + " on " + l.getUri() + "."); + start = System.currentTimeMillis(); + } + + Object result = sendRequest(l, tClass, object, REQUEST_ATTEMPTS); if (isTimed()) { end = System.currentTimeMillis(); long time = end - start; System.out.println("Method " + l.getMethod() + " on " + l.getUri() + " took " + time + " ms."); } + return (T) result; } @@ -190,7 +153,7 @@ public void setBaseUrl(String baseUrl) { } @SuppressWarnings("unchecked") - private T sendRequest(Link l, Class tClass, Object object) throws IOException { + private T sendRequest(Link l, Class tClass, Object object, int attempts) throws IOException { URL serverAddress; String result = ""; HttpURLConnection connection = null; @@ -228,12 +191,22 @@ private T sendRequest(Link l, Class tClass, Object objec if (!useMimeTypes) connection.setRequestProperty("Content-Type", "application/json"); - + + if (l.getRel().equals("authenticate")) { + String authorization = "Basic " + Base64.encodeBase64String((this.ClientKey + ":" + this.ClientSecret).getBytes()); + connection.setRequestProperty("Authorization", authorization); + } + if (tokenData != null) { connection.addRequestProperty("Authorization", tokenData.getTokenType() + " " + tokenData.getAccessToken()); } - - addVersionNumberToHeader(object, url, connection); + + if (connection.getRequestProperty("Accept") == null) + connection.setRequestProperty("Accept", "application/json"); + if (connection.getRequestProperty("Content-Type") == null) + connection.setRequestProperty("Content-Type", "application/json"); + + addVersionNumberToHeader(url, connection); if (method.equals("POST") || method.equals("PUT")) { String json = object != null ? gson.toJson(object) : ""; //should handle the case when POST without object. @@ -247,6 +220,9 @@ private T sendRequest(Link l, Class tClass, Object objec } connection.connect(); + + if (connection.getResponseCode() == -1) + throw new IOException("Invalid HTTP response received."); if (connection.getResponseCode() == HttpURLConnection.HTTP_CREATED || connection.getResponseCode() == HttpURLConnection.HTTP_SEE_OTHER ) { ResponseData data = new ResponseData(); @@ -258,16 +234,18 @@ private T sendRequest(Link l, Class tClass, Object objec } if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { - System.out.println("Authentication expired " + connection.getResponseMessage() + " trying to reauthenticate"); - if (retry && this.tokenData != null) { - this.tokenData = null; - retry = false; - if( authenticate() ) { - System.out.println("Reauthentication success, will continue with " + l.getMethod() + " request on " + l.getRel()); - return sendRequest(l, tClass, object); - } - } - else throw new IOException("Tried to reauthenticate but failed, please check the credentials and status of NFleet-API"); + if (tokenData == null) { + System.out.println("Authentication failed. Make sure you have invoked authenticate() with correct credentials."); + } + else { + System.out.println("Access token has expired. Trying to reauthenticate."); + this.tokenData = null; + if( authenticate() ) { + System.out.println("Reauthentication success, continuing " + l.getRel() + "."); + return sendRequest(l, tClass, object, attempts - 1); + } + else throw new IOException("Reauthentication failed. Please check the credentials and service status."); + } } if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) { @@ -277,35 +255,38 @@ private T sendRequest(Link l, Class tClass, Object objec if (connection.getResponseCode() == HttpURLConnection.HTTP_NO_CONTENT) { return (T) new ResponseData(); } - - if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST && connection.getResponseCode() < HttpURLConnection.HTTP_INTERNAL_ERROR) { - System.out.println("ErrorCode: " + connection.getResponseCode() + " " + connection.getResponseMessage() + - " " + url + ", verb: " + method); - - String errorString = readErrorStreamAndCloseConnection(connection); - throw (NFleetRequestException) gson.fromJson(errorString, NFleetRequestException.class); + + if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST && connection.getResponseCode() < HttpURLConnection.HTTP_INTERNAL_ERROR) { + throw createException(connection); } - else if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR ) { - if (retry) { - System.out.println("Request caused internal server error, waiting "+ RETRY_WAIT_TIME + " ms and trying again."); - return waitAndRetry(connection, l, tClass, object); + else if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR && connection.getResponseCode() < HttpURLConnection.HTTP_BAD_GATEWAY) { + if (attempts > 0) { + int attempt = REQUEST_ATTEMPTS - attempts + 1; + int waiting = attempt * attempt * RETRY_WAIT_TIME_FACTOR; + System.out.println("Request caused internal server error, waiting " + waiting + "ms and trying again (attempt " + attempt + " of " + REQUEST_ATTEMPTS + ")."); + wait(waiting); + return sendRequest(l, tClass, object, attempts - 1); } else { - System.out.println("Requst caused internal server error, please contact dev@nfleet.fi"); - String errorString = readErrorStreamAndCloseConnection(connection); - throw new IOException(errorString); + System.out.println("Request caused internal server error, please contact support at dev@nfleet.fi."); + throw createException(connection); } } - - if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_GATEWAY) { - if (retry) { - System.out.println("Could not connect to NFleet-API, waiting "+ RETRY_WAIT_TIME + " ms and trying again."); - return waitAndRetry(connection, l, tClass, object); + else if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_GATEWAY && connection.getResponseCode() < HttpURLConnection.HTTP_VERSION) { + if (attempts > 0) { + int attempt = REQUEST_ATTEMPTS - attempts + 1; + int waiting = attempt * attempt * UNAVAILABLE_RETRY_WAIT_TIME_FACTOR; + System.out.println("NFleet service is unavailable, waiting " + waiting + "ms and trying again (attempt " + attempt + " of " + REQUEST_ATTEMPTS + ")."); + wait(waiting); + return sendRequest(l, tClass, object, attempts - 1); } else { - System.out.println("Could not connect to NFleet-API, please check service status from http://status.nfleet.fi and try again later."); - String errorString = readErrorStreamAndCloseConnection(connection); - throw new IOException(errorString); + System.out.println("NFleet service is unavailable, please try again later. If the problem persists, contact support at dev@nfleet.fi."); + throw createException(connection); } - + } + else if (connection.getResponseCode() >= HttpURLConnection.HTTP_VERSION) { + System.out.println("Could not connect to NFleet service."); + String errorString = readErrorStreamAndCloseConnection(connection); + throw new IOException(errorString); } result = readDataFromConnection(connection); @@ -330,138 +311,41 @@ else if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR ) objectCache.addUri(url, newEntity); return (T) newEntity; } - - @SuppressWarnings("unchecked") - private T sendRequestWithAddedHeaders(Verb verb, String url, Class tClass, Object object, HashMap headers) throws IOException { - URL serverAddress; - HttpURLConnection connection; - String result = ""; - try { - serverAddress = new URL(url); - connection = (HttpURLConnection) serverAddress.openConnection(); - connection.setInstanceFollowRedirects(false); - boolean doOutput = doOutput(verb); - connection.setDoOutput(doOutput); - connection.setRequestMethod(method(verb)); - connection.setRequestProperty("Authorization", headers.get("authorization")); - connection.addRequestProperty("Accept", "application/json"); - - if (doOutput){ - connection.addRequestProperty("Content-Length", "0"); - OutputStreamWriter os = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); - os.write(""); - os.flush(); - os.close(); - } - connection.connect(); - - if (connection.getResponseCode() == HttpURLConnection.HTTP_SEE_OTHER || connection.getResponseCode() == HttpURLConnection.HTTP_CREATED) { - Link location = parseLocationLinkFromString(connection.getHeaderField("Location")); - Link l = new Link("self", "/tokens", "GET","", true); - ArrayList links = new ArrayList(); - links.add(l); - links.add(location); - ResponseData data = new ResponseData(); - data.setLocation(location); - data.setLinks(links); - return (T) data; - } - - if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { - System.out.println("Authentication expired: " + connection.getResponseMessage()); - if ( retry && this.tokenData != null ) { - retry = false; - this.tokenData = null; - if( authenticate() ) { - System.out.println("Reauthentication success, will continue with " + verb + " request on " + url); - return sendRequestWithAddedHeaders(verb, url, tClass, object, headers); - } - } - else throw new IOException("Tried to reauthenticate but failed, please check the credentials and status of NFleet-API"); - } - - if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST && connection.getResponseCode() < HttpURLConnection.HTTP_INTERNAL_ERROR) { - System.out.println("ErrorCode: " + connection.getResponseCode() + " " + connection.getResponseMessage() + - " " + url + ", verb: " + verb); - - String errorString = readErrorStreamAndCloseConnection(connection); - throw (NFleetRequestException) gson.fromJson(errorString, NFleetRequestException.class); - } - else if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR ) { - if (retry) { - System.out.println("Server responded with internal server error, trying again in " + RETRY_WAIT_TIME + " msec."); - try { - retry = false; - Thread.sleep(RETRY_WAIT_TIME); - return sendRequestWithAddedHeaders(verb, url, tClass, object, headers); - } catch (InterruptedException e) { - - } - } else { - System.out.println("Server responded with internal server error, please contact dev@nfleet.fi"); - } - - String errorString = readErrorStreamAndCloseConnection(connection); - throw new IOException(errorString); - } - - result = readDataFromConnection(connection); - } catch (MalformedURLException e) { - throw e; - } catch (ProtocolException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw e; - } catch (IOException e) { - throw e; - } - return (T) gson.fromJson(result, tClass); - } + + private NFleetRequestException createException(HttpURLConnection connection) throws IOException { + NFleetRequestException ex = null; + String errorString = readErrorStreamAndCloseConnection(connection); + + ex = gson.fromJson(errorString, NFleetRequestException.class); + + if (ex.getItems() == null || ex.getItems().size() == 0) { + ErrorData d = new ErrorData(); + d.setCode(connection.getResponseCode()); + d.setMessage(connection.getResponseMessage()); + List errors = new ArrayList(); + errors.add(d); + ex.setItems(errors); + } + ex.setStatusCode(connection.getResponseCode()); + + return ex; + } private Link getAuthLink() { return new Link("authenticate", "/tokens", "POST", "", true); } - private String method(Verb verb) { - switch (verb) { - case GET: - return "GET"; - case PUT: - return "PUT"; - case POST: - return "POST"; - case DELETE: - return "DELETE"; - case PATCH: - return "PATCH"; - } - return ""; - } - private Link parseLocationLinkFromString(String s) { if (!s.contains("/tokens")) s = s.substring(s.lastIndexOf("/users")); return new Link("location", s, "GET", "", true); } - private boolean doOutput(Verb verb) { - switch (verb) { - case GET: - case DELETE: - return false; - default: - return true; - } - } - private boolean doOutput(String verb) { return (verb.equals("POST") || verb.equals("PUT") || verb.equals("PATCH")); } - private enum Verb { - GET, PUT, POST, DELETE, PATCH - } - + private enum Verb { GET, PUT, POST, DELETE, PATCH } private void addMimeTypeAcceptToRequest(Object object, Class tClass, HttpURLConnection connection) { Field f = null; @@ -518,7 +402,7 @@ private void addMimeTypeContentTypeToRequest(Object object, Class tClass, } } - private void addVersionNumberToHeader(Object object, String url, HttpURLConnection connection) { + private void addVersionNumberToHeader(String url, HttpURLConnection connection) { Field f = null; Object fromCache = null; if (objectCache.containsUri(url)) { @@ -558,7 +442,7 @@ private String readErrorStreamAndCloseConnection(HttpURLConnection connection) { return sb.toString(); } - private String readDataFromConnection(HttpURLConnection connection) { + private String readDataFromConnection(HttpURLConnection connection) throws IOException { InputStream is = null; BufferedReader br = null; StringBuilder sb = null; @@ -576,23 +460,20 @@ private String readDataFromConnection(HttpURLConnection connection) { sb.insert(sb.lastIndexOf("}"), ",\"VersionNumber\":" + eTag + ""); } } catch (IOException e) { - System.out.println("Could not read data from connection"); + System.out.println("Could not read data from connection: " + e.getMessage() ); + throw e; } return sb.toString(); - } - - private T waitAndRetry(HttpURLConnection connection, Link l, Class tClass, Object object) { - try { - retry = false; - Thread.sleep(RETRY_WAIT_TIME); - return sendRequest(l, tClass, object); - } catch (InterruptedException e) { - return null; - } catch (IOException e) { - return null; - } - } - + } + + private void wait(int timeInMilliseconds) + { + try { + Thread.sleep(timeInMilliseconds); + } catch (InterruptedException e) { + // no action + } + } public TokenData getTokenData() { return this.tokenData; @@ -617,5 +498,4 @@ public boolean isTimed() { public void setTimed(boolean timed) { this.timed = timed; } -} - +} \ No newline at end of file diff --git a/fi/cosky/sdk/AddressData.java b/fi/cosky/sdk/AddressData.java index c14afdb..8d24fce 100644 --- a/fi/cosky/sdk/AddressData.java +++ b/fi/cosky/sdk/AddressData.java @@ -1,88 +1,89 @@ -package fi.cosky.sdk; -/* - * This file is subject to the terms and conditions defined in - * file 'LICENSE.txt', which is part of this source code package. - */ -public class AddressData { - - private double Confidence; - private String Resolution; - - private String Region; - private String Country; - private String City; - private String PostalCode; - private String Street; - private int HouseNumber; - private String ApartmentLetter; - private int ApartmentNumber; - - private String AdditionalInfo; - - public double getConfidence() { - return Confidence; - } - public void setConfidence(double confidence) { - Confidence = confidence; - } - public String getResolution() { - return Resolution; - } - public void setResolution(String resolution) { - Resolution = resolution; - } - public String getRegion() { - return Region; - } - public void setRegion(String region) { - Region = region; - } - public int getHouseNumber() { - return HouseNumber; - } - public void setHouseNumber(int houseNumber) { - HouseNumber = houseNumber; - } - public String getApartmentLetter() { - return ApartmentLetter; - } - public void setApartmentLetter(String apartmentLetter) { - ApartmentLetter = apartmentLetter; - } - public int getApartmentNumber() { - return ApartmentNumber; - } - public void setApartmentNumber(int apartmentNumber) { - ApartmentNumber = apartmentNumber; - } - public String getAdditionalInfo() { - return AdditionalInfo; - } - public void setAdditionalInfo(String additionalInfo) { - AdditionalInfo = additionalInfo; - } - public String getCountry() { - return Country; - } - public void setCountry(String country) { - Country = country; - } - public String getCity() { - return City; - } - public void setCity(String city) { - City = city; - } - public String getPostalCode() { - return PostalCode; - } - public void setPostalCode(String postalCode) { - PostalCode = postalCode; - } - public String getStreet() { - return Street; - } - public void setStreet(String street) { - Street = street; - } -} +package fi.cosky.sdk; + +import java.util.HashSet; + +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.txt', which is part of this source code package. + */ +public class AddressData { + + private double Confidence; + private int Resolution; + + private String Region; + private String Country; + private String City; + private String PostalCode; + private String Street; + private int HouseNumber; + private String ApartmentLetter; + private int ApartmentNumber; + + private String AdditionalInfo; + + public double getConfidence() { + return Confidence; + } + public void setConfidence(double confidence) { + Confidence = confidence; + } + public HashSet getResolution() { + return AddressResolution.toAddressResolutionSet(Resolution); + } + public void setResolution(int resolution) { Resolution = resolution; } + public String getRegion() { + return Region; + } + public void setRegion(String region) { + Region = region; + } + public int getHouseNumber() { + return HouseNumber; + } + public void setHouseNumber(int houseNumber) { + HouseNumber = houseNumber; + } + public String getApartmentLetter() { + return ApartmentLetter; + } + public void setApartmentLetter(String apartmentLetter) { + ApartmentLetter = apartmentLetter; + } + public int getApartmentNumber() { + return ApartmentNumber; + } + public void setApartmentNumber(int apartmentNumber) { + ApartmentNumber = apartmentNumber; + } + public String getAdditionalInfo() { + return AdditionalInfo; + } + public void setAdditionalInfo(String additionalInfo) { + AdditionalInfo = additionalInfo; + } + public String getCountry() { + return Country; + } + public void setCountry(String country) { + Country = country; + } + public String getCity() { + return City; + } + public void setCity(String city) { + City = city; + } + public String getPostalCode() { + return PostalCode; + } + public void setPostalCode(String postalCode) { + PostalCode = postalCode; + } + public String getStreet() { + return Street; + } + public void setStreet(String street) { + Street = street; + } +} diff --git a/fi/cosky/sdk/AddressResolution.java b/fi/cosky/sdk/AddressResolution.java new file mode 100644 index 0000000..d1800d4 --- /dev/null +++ b/fi/cosky/sdk/AddressResolution.java @@ -0,0 +1,42 @@ +package fi.cosky.sdk; + +import java.util.HashSet; + +public enum AddressResolution { + None(0), + Coordinate(1), + City(2), + PostalCode(4), + PostalCodeAfterCity(8), + Street(16), + HouseNumber(32), + Inexact(64), + Ambiguous(128), + HouseNumberOutOfRange(256), + HouseNumberNotGiven(512), + HouseNumbersMissingFromStreetData(1024), + HouseNumberNonexisting(2048); + + private int value; + + private AddressResolution(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static HashSet toAddressResolutionSet(int value) { + char[] binaryCharArray = Integer.toBinaryString(value).toCharArray(); + HashSet result = new HashSet(); + for (int i = 0; i < binaryCharArray.length; i++) { + if (binaryCharArray[i] == '1') { + AddressResolution ar = AddressResolution.values()[i]; + result.add(ar); + } + } + + return result; + } +} diff --git a/fi/cosky/sdk/AppService.java b/fi/cosky/sdk/AppService.java index ff4a87a..7d9a27f 100644 --- a/fi/cosky/sdk/AppService.java +++ b/fi/cosky/sdk/AppService.java @@ -11,6 +11,8 @@ import java.net.ProtocolException; import java.net.URL; import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; import com.google.gson.*; @@ -21,11 +23,12 @@ public class AppService { private String baseUrl; private String ClientKey; private String ClientSecret; - private boolean retry; public AppUserDataSet Root; private String user; private String password; - private static int RETRY_WAIT_TIME = 2000; + private static int RETRY_WAIT_TIME_FACTOR = 1000; + private static int UNAVAILABLE_RETRY_WAIT_TIME_FACTOR = 10000; + private static int REQUEST_ATTEMPTS = 3; private String appServiceUrl; private AppTokenData token; private int currentAppUserId; @@ -35,14 +38,10 @@ public class AppService { public AppService(String appServiceUrl,String appUrl, String clientKey, String clientSecret) { this.baseUrl = appServiceUrl + "/appusers"; - this.retry = true; this.ClientKey = clientKey; this.ClientSecret = clientSecret; this.appServiceUrl = appServiceUrl; this.appUrl = appUrl; - //Delete-Verb causes connection to keep something somewhere that causes the next request to fail. - //this hopefully helps with that. - System.setProperty("http.keepAlive", "false"); this.Root = getAppUserDataSet(); } @@ -77,14 +76,13 @@ public T navigate(Class tClass, Link l, String user, Str @SuppressWarnings("unchecked") public T navigate(Class tClass, Link l, HashMap queryParameters, String user, String password) throws IOException { Object result; - retry = true; String uri = l.getUri(); //check if (l.getMethod().equals("DELETE")) { - result = sendRequest(l, tClass, new Object(), user, password); + result = sendRequest(l, tClass, new Object(), user, password, REQUEST_ATTEMPTS); } else { - result = sendRequest(l, tClass, null, user, password); + result = sendRequest(l, tClass, null, user, password, REQUEST_ATTEMPTS); } return (T) result; } @@ -100,11 +98,11 @@ public T navigate(Class tClass, Link l, HashMap T navigate(Class tClass, Link l, Object object, String user, String password) throws IOException { - return (T) sendRequest(l, tClass, object, user, password); + return (T) sendRequest(l, tClass, object, user, password, REQUEST_ATTEMPTS); } public T navigate(Class tClass, Link l, Object object) throws IOException { - return (T) sendRequest(l, tClass, object, null, null); + return (T) sendRequest(l, tClass, object, null, null, REQUEST_ATTEMPTS); } public Link getRoot() { return new Link("self", baseUrl, "GET","", true); @@ -118,7 +116,7 @@ public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; } - private T sendRequest(Link l, Class tClass, Object object, String user, String password) throws IOException { + private T sendRequest(Link l, Class tClass, Object object, String user, String password, int attempts) throws IOException { URL serverAddress; BufferedReader br; String result = ""; @@ -154,8 +152,16 @@ private T sendRequest(Link l, Class tClass, Object objec osw.flush(); osw.close(); } + + if (connection.getRequestProperty("Accept") == null) + connection.setRequestProperty("Accept", "application/json"); + if (connection.getRequestProperty("Content-Type") == null) + connection.setRequestProperty("Content-Type", "application/json"); connection.connect(); + + if (connection.getResponseCode() == -1) + throw new IOException("Invalid HTTP response received."); if (connection.getResponseCode() == HttpURLConnection.HTTP_CREATED || connection.getResponseCode() == HttpURLConnection.HTTP_SEE_OTHER ) { ResponseData data = new ResponseData(); @@ -175,36 +181,39 @@ private T sendRequest(Link l, Class tClass, Object objec } if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST && connection.getResponseCode() < HttpURLConnection.HTTP_INTERNAL_ERROR) { - System.out.println("App: ErrorCode: " + connection.getResponseCode() + " " + connection.getResponseMessage() + - " " + url + ", verb: " + method); - - String errorString = readErrorStreamAndCloseConnection(connection); - throw (NFleetRequestException) gson.fromJson(errorString, NFleetRequestException.class); - } - else if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR ) { - if (retry) { - - System.out.println("App: Request caused internal server error, waiting "+ RETRY_WAIT_TIME + " ms and trying again."); - System.out.println("Url was " + url); - return waitAndRetry(connection, l, tClass, object, user, password); - } else { - System.out.println("App: Request caused internal server error, please contact dev@nfleet.fi"); - String errorString = readErrorStreamAndCloseConnection(connection); - throw new IOException(errorString); - } - } - - if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_GATEWAY) { - if (retry) { - System.out.println("App: Could not connect to NFleet-API, waiting "+ RETRY_WAIT_TIME + " ms and trying again."); - return waitAndRetry(connection, l, tClass, object, user, password); - } else { - System.out.println("App: Could not connect to NFleet-API, please check service status from http://status.nfleet.fi and try again later."); - String errorString = readErrorStreamAndCloseConnection(connection); - throw new IOException(errorString); - } - + System.out.println("App: " + connection.getResponseCode() + " " + connection.getResponseMessage() + " " + url + ", verb: " + method); + createException(connection); } + else if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR && connection.getResponseCode() < HttpURLConnection.HTTP_BAD_GATEWAY) { + if (attempts > 0) { + int attempt = REQUEST_ATTEMPTS - attempts + 1; + int waiting = attempt * attempt * RETRY_WAIT_TIME_FACTOR; + System.out.println("App: Request caused internal server error, waiting " + waiting + "ms and trying again (attempt " + attempt + " of " + REQUEST_ATTEMPTS + ")."); + wait(waiting); + return sendRequest(l, tClass, object, user, password, attempts - 1); + } else { + System.out.println("App: Request caused internal server error, please contact support at dev@nfleet.fi."); + String errorString = readErrorStreamAndCloseConnection(connection); + createException(connection); + } + } + else if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_GATEWAY && connection.getResponseCode() < HttpURLConnection.HTTP_VERSION) { + if (attempts > 0) { + int attempt = REQUEST_ATTEMPTS - attempts + 1; + int waiting = attempt * attempt * UNAVAILABLE_RETRY_WAIT_TIME_FACTOR; + System.out.println("App: NFleet App is unavailable, waiting " + waiting + "ms and trying again (attempt " + attempt + " of " + REQUEST_ATTEMPTS + ")."); + wait(waiting); + return sendRequest(l, tClass, object, user, password, attempts - 1); + } else { + System.out.println("App: NFleet App is unavailable, please try again later. If the problem persists, contact support at dev@nfleet.fi."); + createException(connection); + } + } + else if (connection.getResponseCode() >= HttpURLConnection.HTTP_VERSION) { + System.out.println("App: Could not connect to NFleet App."); + String errorString = readErrorStreamAndCloseConnection(connection); + throw new IOException(errorString); + } result = readDataFromConnection(connection); @@ -226,6 +235,25 @@ else if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR ) } return (T) gson.fromJson(result, tClass); } + + private NFleetRequestException createException(HttpURLConnection connection) throws IOException { + NFleetRequestException ex = null; + String errorString = readErrorStreamAndCloseConnection(connection); + + ex = gson.fromJson(errorString, NFleetRequestException.class); + + if (ex.getItems() == null || ex.getItems().size() == 0) { + ErrorData d = new ErrorData(); + d.setCode(connection.getResponseCode()); + d.setMessage(connection.getResponseMessage()); + List errors = new ArrayList(); + errors.add(d); + ex.setItems(errors); + } + ex.setStatusCode(connection.getResponseCode()); + + return ex; + } private String method(Verb verb) { switch (verb) { @@ -309,17 +337,14 @@ private String readDataFromConnection(HttpURLConnection connection) { return sb.toString(); } - private T waitAndRetry(HttpURLConnection connection, Link l, Class tClass, Object object, String user, String password) { - try { - retry = false; - Thread.sleep(RETRY_WAIT_TIME); - return sendRequest(l, tClass, object, user, password); - } catch (InterruptedException e) { - return null; - } catch (IOException e) { - return null; - } - } + private void wait(int timeInMilliseconds) + { + try { + Thread.sleep(timeInMilliseconds); + } catch (InterruptedException e) { + // no action + } + } public boolean Login(String user, String password) throws IOException { Link l = new Link("signin", appServiceUrl + "/signin", "GET","" , true); diff --git a/fi/cosky/sdk/NFleetRequestException.java b/fi/cosky/sdk/NFleetRequestException.java index 5f66c69..c93ca4c 100644 --- a/fi/cosky/sdk/NFleetRequestException.java +++ b/fi/cosky/sdk/NFleetRequestException.java @@ -10,6 +10,7 @@ public class NFleetRequestException extends IOException { */ private static final long serialVersionUID = 1L; private List Items; + private int statusCode; public NFleetRequestException() { Items = new ArrayList(); @@ -26,12 +27,20 @@ public List getItems() { public void setItems(List items) { Items = items; - } + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } @Override public String toString() { StringBuilder sb = new StringBuilder(); - + sb.append("Status: " + getStatusCode()); for( ErrorData e : Items) { sb.append(" ErrorCode: "+e.getCode() + " ErrorMessage: " + e.getMessage()); } diff --git a/fi/cosky/sdk/RouteEventData.java b/fi/cosky/sdk/RouteEventData.java index 23fc3ab..592d3c5 100644 --- a/fi/cosky/sdk/RouteEventData.java +++ b/fi/cosky/sdk/RouteEventData.java @@ -21,12 +21,21 @@ public class RouteEventData extends BaseData { private Date DepartureTime; private String State; private KPIData KPIs; + private LocationData Location; private List Geometry; private String Type; + private String Style; private int SequenceNumber; private int TaskId; - - int getVersionNumber() { + private String LockState; + private String TimeState; + private String Info; + private String Info2; + private String Info3; + private String Info4; + + + int getVersionNumber() { return VersionNumber; } @@ -97,6 +106,14 @@ public void setKPIs(KPIData kPIs) { KPIs = kPIs; } + public LocationData getLocation() { + return Location; + } + + public void setLocation(LocationData location) { + Location = location; + } + public List getGeometry() { return Geometry; } @@ -113,6 +130,14 @@ public void setType(String type) { Type = type; } + public String getStyle() { + return Style; + } + + public void setStyle(String style) { + Style = style; + } + public int getSequenceNumber() { return SequenceNumber; } @@ -127,5 +152,53 @@ public int getTaskId() { public void setTaskId(int taskId) { TaskId = taskId; - } + } + + public String getTimeState() { + return TimeState; + } + + public void setTimeState(String timeState) { + TimeState = timeState; + } + + public String getLockState() { + return LockState; + } + + public void setLockState(String lockState) { + LockState = lockState; + } + + public String getInfo() { + return Info; + } + + public void setInfo(String info) { + Info = info; + } + + public String getInfo2() { + return Info2; + } + + public void setInfo2(String info2) { + Info2 = info2; + } + + public String getInfo3() { + return Info3; + } + + public void setInfo3(String info3) { + Info3 = info3; + } + + public String getInfo4() { + return Info4; + } + + public void setInfo4(String info4) { + Info4 = info4; + } } diff --git a/fi/cosky/sdk/TaskEventData.java b/fi/cosky/sdk/TaskEventData.java index 8f34313..2509bd5 100644 --- a/fi/cosky/sdk/TaskEventData.java +++ b/fi/cosky/sdk/TaskEventData.java @@ -10,7 +10,9 @@ public class TaskEventData extends BaseData { private int Id; private Type Type; private String Info; + private String Name; private State State; + private String Style; private List TimeWindows; private LocationData Location; private int ServiceTime; @@ -32,6 +34,14 @@ public int getId() { public void setId(int id) { Id = id; } + + public String getName() { + return Name; + } + + public void setName(String name) { + this.Name = name; + } public String getInfo() { return Info; @@ -56,6 +66,14 @@ public State getState() { public void setState(State state) { State = state; } + + public String getStyle() { + return Style; + } + + public void setStyle(String style) { + Style = style; + } public List getTimeWindows() { return TimeWindows; diff --git a/fi/cosky/sdk/TaskEventUpdateRequest.java b/fi/cosky/sdk/TaskEventUpdateRequest.java index 0025e71..6970bca 100644 --- a/fi/cosky/sdk/TaskEventUpdateRequest.java +++ b/fi/cosky/sdk/TaskEventUpdateRequest.java @@ -1,5 +1,6 @@ package fi.cosky.sdk; import java.util.List; +import java.util.Date; /* * This file is subject to the terms and conditions defined in @@ -9,12 +10,17 @@ public class TaskEventUpdateRequest extends BaseData { private int TaskEventId; private Type Type; + private String Name; private List TimeWindows; private LocationData Location; private int ServiceTime; private int StoppingTime; private List Capacities; - + private String VehicleId; + private String Style; + private int SequenceNumber; + private boolean IsLocked; + private Date PresetArrivalTime; //Constructor uses only the required fields, others can be accessed via getters and setters public TaskEventUpdateRequest(Type type, LocationData location, List capacities) { @@ -30,6 +36,15 @@ public Type getType() { public void setType(Type type) { this.Type = type; } + + public String getName() { + return Name; + } + + public void setName(String name) { + this.Name = name; + } + public int getTaskEventId() { return TaskEventId; } @@ -77,4 +92,44 @@ public List getCapacities() { public void setCapacities(List capacities) { Capacities = capacities; } + + public void setVehicleId(String vehicleId) { + VehicleId = vehicleId; + } + + public String getVehicleId() { + return VehicleId; + } + + public void setStyle(String style) { + Style = style; + } + + public String getStyle() { + return Style; + } + + public void setSequenceNumber(int sequenceNumber) { + SequenceNumber = sequenceNumber; + } + + public int getSequenceNumber() { + return SequenceNumber; + } + + public void setIsLocked(boolean isLocked) { + IsLocked = isLocked; + } + + public boolean getIsLocked() { + return IsLocked; + } + + public Date getPresetArrivalTime() { + return PresetArrivalTime; + } + + public void setPresetArrivalTime(Date presetArrivalTime) { + PresetArrivalTime = presetArrivalTime; + } } diff --git a/fi/cosky/sdk/TaskUpdateRequest.java b/fi/cosky/sdk/TaskUpdateRequest.java index 1d6cca1..19c3046 100644 --- a/fi/cosky/sdk/TaskUpdateRequest.java +++ b/fi/cosky/sdk/TaskUpdateRequest.java @@ -22,6 +22,7 @@ public class TaskUpdateRequest extends BaseData { private int UserId; private List IncompatibleVehicleTypes; private List CompatibleVehicleTypes; + private Boolean IsLockedToVehicle; @Deprecated private double Profit; @@ -169,5 +170,12 @@ public void setRelocationType(String relocationType) { RelocationType = relocationType; } + public Boolean getIsLockedToVehicle() { + return IsLockedToVehicle; + } + + public void setIsLockedToVehicle(Boolean isLockedToVehicle) { + IsLockedToVehicle = isLockedToVehicle; + } } diff --git a/fi/cosky/sdk/VehicleData.java b/fi/cosky/sdk/VehicleData.java index 8015fa3..0b83e9a 100644 --- a/fi/cosky/sdk/VehicleData.java +++ b/fi/cosky/sdk/VehicleData.java @@ -8,7 +8,7 @@ public class VehicleData extends BaseData { public static final String MimeType = "application/vnd.jyu.nfleet.vehicle"; - public static final double MimeVersion = 2.1; + public static final double MimeVersion = 2.2; private int Id; private String Name; @@ -22,6 +22,7 @@ public class VehicleData extends BaseData { private String SpeedProfile; private double SpeedFactor; private String RelocationType; + private String Info1; private String ActivityState; @@ -29,6 +30,9 @@ public class VehicleData extends BaseData { private double KilometerCost; private double HourCost; + private CoordinateData CurrentLocation; + + public RouteData getRoute() { return Route; } @@ -177,7 +181,27 @@ public VehicleUpdateRequest toRequest() { request.setVehicleSpeedProfile(SpeedProfile); request.setRelocationType(RelocationType); request.setActivityState(ActivityState); + request.setInfo1(Info1); + request.setHourCost(HourCost); + request.setFixedCost(FixedCost); + request.setKilometerCost(KilometerCost); + request.setVehicleType(VehicleType); return request; } + public CoordinateData getCurrentLocation() { + return CurrentLocation; + } + + public void setCurrentLocation(CoordinateData currentLocation) { + CurrentLocation = currentLocation; + } + + public String getInfo1() { + return Info1; + } + + public void setInfo1(String info1) { + Info1 = info1; + } } diff --git a/fi/cosky/sdk/VehicleUpdateRequest.java b/fi/cosky/sdk/VehicleUpdateRequest.java index cdce9b7..0aa84d0 100644 --- a/fi/cosky/sdk/VehicleUpdateRequest.java +++ b/fi/cosky/sdk/VehicleUpdateRequest.java @@ -21,8 +21,11 @@ public class VehicleUpdateRequest extends BaseData { private String SpeedProfile; private double SpeedFactor; private String RelocationType; - private String ActivityState; - + private String ActivityState; + private String Info1; + private CoordinateData CurrentLocation; + private Boolean IsLocked; + private double FixedCost; private double KilometerCost; private double HourCost; @@ -185,4 +188,28 @@ public String getActivityState() { public void setActivityState(String activityState) { ActivityState = activityState; } + + public String getInfo1() { + return Info1; + } + + public void setInfo1(String info1) { + Info1 = info1; + } + + public CoordinateData getCurrentLocation() { + return CurrentLocation; + } + + public void setCurrentLocation(CoordinateData currentLocation) { + CurrentLocation = currentLocation; + } + + public Boolean getIsLocked() { + return IsLocked; + } + + public void setIsLocked(Boolean isLocked) { + IsLocked = isLocked; + } } diff --git a/fi/cosky/sdk/tests/SdkTests.java b/fi/cosky/sdk/tests/SdkTests.java index 2cfe4a0..39d5e24 100644 --- a/fi/cosky/sdk/tests/SdkTests.java +++ b/fi/cosky/sdk/tests/SdkTests.java @@ -10,6 +10,7 @@ import java.util.Date; import java.util.List; import java.util.UUID; +import java.util.*; import fi.cosky.sdk.*; import fi.cosky.sdk.CoordinateData.CoordinateSystem; @@ -110,22 +111,18 @@ public void T04CreatingTaskTest() { UserData user = TestHelper.getOrCreateUser(api); RoutingProblemData problem = TestHelper.createProblemWithDemoData(api, user); TaskData task = null; - TaskUpdateRequest update = null; + TaskUpdateRequest update2 = null; try { //##BEGIN EXAMPLE creatingtask## CoordinateData pickup = new CoordinateData(54.14454,12.108808,CoordinateSystem.Euclidian); - LocationData pickupLocation = new LocationData(); pickupLocation.setCoordinatesData(pickup); CoordinateData delivery = new CoordinateData(53.545867,10.276409,CoordinateSystem.Euclidian); - LocationData deliveryLocation = new LocationData(); deliveryLocation.setCoordinatesData(delivery); - ArrayList capacities = new ArrayList(); - capacities.add(new CapacityData("Weight", 100000)); ArrayList timeWindows = new ArrayList(); Date morning = new Date(); morning.setHours(7); @@ -139,7 +136,7 @@ public void T04CreatingTaskTest() { ArrayList taskEvents = new ArrayList(); taskEvents.add(new TaskEventUpdateRequest(Type.Pickup, pickupLocation, taskCapacity)); taskEvents.add(new TaskEventUpdateRequest(Type.Delivery, deliveryLocation, taskCapacity)); - update = new TaskUpdateRequest(taskEvents); + TaskUpdateRequest update = new TaskUpdateRequest(taskEvents); update.setName("testTask"); taskEvents.get(0).setTimeWindows(timeWindows); taskEvents.get(1).setTimeWindows(timeWindows); @@ -149,13 +146,13 @@ public void T04CreatingTaskTest() { ResponseData result = api.navigate(ResponseData.class, problem.getLink("create-task"), update); //##END EXAMPLE## - + update2 = update; task = api.navigate(TaskData.class, result.getLocation()); } catch (Exception e) { System.out.println(e.toString()); } - assertEquals(task.getName(), update.getName()); + assertEquals(task.getName(), update2.getName()); } @SuppressWarnings("unused") @@ -311,20 +308,20 @@ public void T11StartingOptimizationTest() { API api = TestHelper.authenticate(); UserData user = TestHelper.getOrCreateUser(api); RoutingProblemData problem = TestHelper.createProblemWithDemoData(api, user); - ResponseData response = null; + ResponseData result = null; try { problem = api.navigate(RoutingProblemData.class, problem.getLink("self")); //##BEGIN EXAMPLE startingopt## RoutingProblemUpdateRequest update = problem.toRequest(); update.setState("Running"); - ResponseData result = api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), update); + ResponseData response = api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), update); //##END EXAMPLE## - response = result; + result = response; } catch (Exception e) { System.out.println(e.toString()); } - assertNotNull(response); + assertNotNull(result); } @Test @@ -338,13 +335,14 @@ public void T12StoppingOptimizationTest() { RoutingProblemUpdateRequest update = problem.toRequest(); update.setState("Running"); - result = api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), update); + api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), update); //##BEGIN EXAMPLE stoppingopt## RoutingProblemUpdateRequest updateRequest = problem.toRequest(); updateRequest.setState("Stopped"); - result = api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), updateRequest); + ResponseData response = api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), updateRequest); //##END EXAMPLE## + result = response; } catch (Exception e) { System.out.println(e.toString()); } @@ -387,10 +385,6 @@ public void T14GetProgressTest() { try { //##BEGIN EXAMPLE getprogress## ResponseData response = api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), update); - - Thread.sleep(5000); - - problem = api.navigate(RoutingProblemData.class, response.getLocation()); while ( problem.getProgress() < 100 ) { @@ -785,23 +779,33 @@ public void T25ApplyImportTest() { assertNotEquals(response.getLocation(), null); } + @Test public void T26TestGeocodingThruAPI() { API api = TestHelper.authenticate(); UserData user = TestHelper.getOrCreateUser(api); RoutingProblemData routingProblemData = TestHelper.createProblem(api, user); - VehicleUpdateRequest vehicle = TestHelper.createVehicleUpdateRequest("TestiAuto"); + VehicleUpdateRequest vehicle = TestHelper.createVehicleUpdateRequestWithAddress("TestiAuto"); VehicleData response = null; try { ResponseData res = api.navigate(ResponseData.class, routingProblemData.getLink("create-vehicle"), vehicle); - System.out.println(res); + routingProblemData = api.navigate(RoutingProblemData.class, routingProblemData.getLink("self")); + while (!routingProblemData.getDataState().equals("Ready")) { + Thread.sleep(1000); + routingProblemData = api.navigate(RoutingProblemData.class, routingProblemData.getLink("self")); + } response = api.navigate(VehicleData.class, res.getLocation()); } catch (Exception e) { System.out.println(e.toString()); } System.out.println(response); + Iterator i = response.getStartLocation().getAddress().getResolution().iterator(); + while (i.hasNext()) { + System.out.println(i.next()); + } assertNotEquals(0, response.getEndLocation().getCoordinate().getLatitude()); + } @Test @@ -1436,7 +1440,7 @@ public void T41CreateDepot() { RoutingProblemData problem = TestHelper.createProblemWithDemoData(api, user); LocationData location = new LocationData(); - location.setCoordinatesData(new CoordinateData( 0.0, 0.0, CoordinateSystem.Euclidian )); + location.setCoordinatesData(new CoordinateData( 62.231023, 25.698652, CoordinateSystem.WGS84 )); ArrayList capacities = new ArrayList(); capacities.add(new CapacityData("Weight", 10)); @@ -1467,7 +1471,7 @@ public void T42CreateDepotSet() { RoutingProblemData problem = TestHelper.createProblemWithDemoData(api, user); LocationData location = new LocationData(); - location.setCoordinatesData(new CoordinateData( 0.0, 0.0, CoordinateSystem.Euclidian )); + location.setCoordinatesData(new CoordinateData( 62.231023, 25.698652, CoordinateSystem.WGS84 )); ArrayList capacities = new ArrayList(); capacities.add(new CapacityData("Weight", 10)); @@ -1511,7 +1515,7 @@ public void T43UpdateDepot() { RoutingProblemData problem = TestHelper.createProblemWithDemoData(api, user); LocationData location = new LocationData(); - location.setCoordinatesData(new CoordinateData( 0.0, 0.0, CoordinateSystem.Euclidian )); + location.setCoordinatesData(new CoordinateData( 62.231023, 25.698652, CoordinateSystem.WGS84 )); ArrayList capacities = new ArrayList(); capacities.add(new CapacityData("Weight", 10)); @@ -1838,4 +1842,236 @@ public void T48TestCreatingAppServiceUsers() { assertEquals(1, users.Items.size()); } */ + + @Test + public void T49UpdatingVehicleLocationTest() { + API api = TestHelper.authenticate(); + UserData user = TestHelper.getOrCreateUser(api); + RoutingProblemData problem = TestHelper.createProblem(api, user); + + VehicleData vehicle = TestHelper.getVehicle(api, user, problem); + + CoordinateData currentLocation = new CoordinateData(); + currentLocation.setLatitude(61.4938); + currentLocation.setLongitude(26.523); + currentLocation.setSystem(CoordinateSystem.Euclidian); + + VehicleUpdateRequest update = vehicle.toRequest(); + update.setCurrentLocation(currentLocation); + try { + vehicle.setCurrentLocation(currentLocation); + api.navigate(ResponseData.class, vehicle.getLink("update"), update); + + vehicle = api.navigate(VehicleData.class, vehicle.getLink("self")); + } catch (Exception e) { + + } + assertNotNull(vehicle.getCurrentLocation()); + assertEquals(vehicle.getCurrentLocation().getLatitude(), 61.4938, 0.0001); + assertEquals(vehicle.getCurrentLocation().getLongitude(), 26.523, 0.001); + } + + @Test + public void T50UpdatingASpecificTaskAfterImport() { + API api = TestHelper.authenticate(); + UserData user = TestHelper.getOrCreateUser(api); + RoutingProblemData problem = TestHelper.createProblem(api, user); + + TaskSetImportRequest tasks = new TaskSetImportRequest(); + tasks.setItems(TestHelper.createListOfTasks(10)); + + VehicleSetImportRequest vehicles = new VehicleSetImportRequest(); + vehicles.setItems(TestHelper.createListOfVehicles(3)); + + ImportRequest im = new ImportRequest(); + im.setVehicles(vehicles); + im.setTasks(tasks); + + try { + ResponseData response = api.navigate(ResponseData.class, problem.getLink("import-data"), im); + + ImportData result = api.navigate(ImportData.class, response.getLocation()); + + response = api.navigate(ResponseData.class, result.getLink("apply-import")); + + RoutingProblemData routingProblemData = api.navigate(RoutingProblemData.class, problem.getLink("self")); + + while (routingProblemData.getDataState().equals("Pending")) { + System.out.println("State is pending"); + Thread.sleep(1000); + routingProblemData = api.navigate(RoutingProblemData.class, routingProblemData.getLink("self")); + } + + TaskDataSet t = api.navigate(TaskDataSet.class, problem.getLink("list-tasks")); + + //now in tasksToSelfLink there is relation between given ids in info fields and NFleet given self links + HashMap tasksToSelfLink = new HashMap(); + for (TaskData td : t.getItems()) { + tasksToSelfLink.put(td.getInfo(), td.getId()); + } + + //optimize to get routes for vehicles + RoutingProblemUpdateRequest update = problem.toRequest(); + update.setState("Running"); + response = api.navigate(ResponseData.class, problem.getLink("toggle-optimization"), update); + problem = api.navigate(RoutingProblemData.class, response.getLocation()); + while (problem.getState().equals("Running")) { + Thread.sleep(1000); + problem = api.navigate(RoutingProblemData.class, response.getLocation()); + System.out.println(problem.getProgress()); + } + + //lets say we need to update task with Info of "task 5", so first get the NFleet id of it + int id = tasksToSelfLink.get("task 5"); + System.out.println("finding task" + id); + + PlanData plan = api.navigate(PlanData.class, problem.getLink("plan")); + + //this time we do not know which vehicle has the task so lets find it + + for (VehiclePlanData vpd : plan.getItems()) { + for (RouteEventData route : vpd.getEvents()){ + if (route.getTaskId() == id) { + // now we found the task and can do something to it, for example lock it + System.out.println("found task " + id + " on vehicle " + vpd.getName()); + RouteEventData event = api.navigate(RouteEventData.class, route.getLink("self")); + System.out.println("lock state " + event.getLockState()); + RouteEventUpdateRequest req = new RouteEventUpdateRequest(); + req.setState("Locked"); + api.navigate(ResponseData.class, event.getLink("lock"), req); + + event = api.navigate(RouteEventData.class, route.getLink("self")); + System.out.println("lock state " + event.getLockState() + " " + event.getArrivalTime()); + + Calendar calendar = Calendar.getInstance(); + + calendar.set(Calendar.HOUR_OF_DAY, 10); + Date startD = calendar.getTime(); + + req = new RouteEventUpdateRequest(); + req.setActualArrivalTime(startD); + //if route event is locked with a lock, it needs to be removed when setting arrival time + req.setState(null); + + api.navigate(ResponseData.class, event.getLink("lock"), req); + + event = api.navigate(RouteEventData.class, route.getLink("self")); + System.out.println("lock state " + event.getLockState() + " " + event.getArrivalTime()); + + break; + } + } + } + + } catch (Exception e) { + + } + } + @Test + public void T51CreateTaskWithAddress() { + API api = TestHelper.authenticate(); + UserData user = TestHelper.getOrCreateUser(api); + RoutingProblemData problem = TestHelper.createProblem(api, user); + TaskData created = null; + try { + + //##BEGIN EXAMPLE creatingtaskwithaddress## + AddressData pickup = new AddressData(); + pickup.setCity("Jyväskylä"); + pickup.setCountry("Finland"); + pickup.setPostalCode("40630"); + pickup.setStreet("Pajatie"); + pickup.setApartmentNumber(8); + pickup.setApartmentLetter("F"); + + LocationData pickupdata = new LocationData(); + pickupdata.setAddress(pickup); + + AddressData delivery = new AddressData(); + delivery.setCity("Jyväskylä"); + delivery.setCountry("Finland"); + delivery.setPostalCode("40100"); + delivery.setStreet("Mattilanniemi"); + delivery.setApartmentNumber(2); + + LocationData deliverydata = new LocationData(); + deliverydata.setAddress(delivery); + ArrayList timeWindows = new ArrayList(); + Date morning = new Date(); + morning.setHours(7); + Date evening = new Date(); + evening.setHours(16); + timeWindows.add(new TimeWindowData(morning, evening)); + + ArrayList taskCapacity = new ArrayList(); + taskCapacity.add(new CapacityData("Weight", 1)); + + ArrayList taskEvents = new ArrayList(); + taskEvents.add(new TaskEventUpdateRequest(Type.Pickup, pickupdata, taskCapacity)); + taskEvents.add(new TaskEventUpdateRequest(Type.Delivery, deliverydata, taskCapacity)); + TaskUpdateRequest update = new TaskUpdateRequest(taskEvents); + update.setName("testTask"); + taskEvents.get(0).setTimeWindows(timeWindows); + taskEvents.get(1).setTimeWindows(timeWindows); + taskEvents.get(0).setServiceTime(10); + taskEvents.get(1).setServiceTime(10); + update.setActivityState("Active"); + + ResponseData response = api.navigate(ResponseData.class, problem.getLink("create-task"), update); + + //##END EXAMPLE## + + created = api.navigate(TaskData.class, response.getLocation()); + } catch (Exception e) { + System.out.println(e); + } + Assert.assertNotNull(created); + } + + @Test + public void T52GetRouteEvents() { + API api = TestHelper.authenticate(); + UserData user = TestHelper.getOrCreateUser(api); + RoutingProblemData problem = TestHelper.createProblemWithDemoData(api, user); + RouteEventDataSet result = null; + try { + VehicleData vehicle = api.navigate(VehicleDataSet.class, problem.getLink("list-vehicles")).getItems().get(0); + //##BEGIN EXAMPLE getrouteEvents## + RouteEventDataSet events = api.navigate(RouteEventDataSet.class, vehicle.getLink("list-events")); + //##END EXAMPLE## + result = events; + } catch (Exception e) { + System.out.println(e); + } + assertNotNull(result); + } + + @Test + public void T53InvalidVersionNumberTest() { + API api = TestHelper.authenticate(); + UserData user = TestHelper.getOrCreateUser(api); + RoutingProblemData problem = TestHelper.createProblemWithDemoData(api, user); + + NFleetRequestException exception = null; + try { + VehicleData vehicle = api.navigate(VehicleData.class, api.navigate(VehicleDataSet.class, problem.getLink("list-vehicles")).getItems().get(0).getLink("self")); + //##BEGIN EXAMPLE invalidversionnumber## + + VehicleUpdateRequest update = vehicle.toRequest(); + update.setName("new"); + + api.navigate(ResponseData.class, vehicle.getLink("update"), update); + + update.setName("invalid"); + api.navigate(ResponseData.class, vehicle.getLink("update"), update); + } catch (NFleetRequestException e) { + assertEquals(412, e.getItems().get(0).getCode()); + + //##END EXAMPLE## + exception = e; + } catch (IOException e) { + System.out.println(e); + } + Assert.assertNotNull(exception); + } } diff --git a/fi/cosky/sdk/tests/TestData.java b/fi/cosky/sdk/tests/TestData.java index b4fbfea..6f86bc2 100644 --- a/fi/cosky/sdk/tests/TestData.java +++ b/fi/cosky/sdk/tests/TestData.java @@ -17,19 +17,8 @@ public class TestData { @SuppressWarnings("deprecation") public static void CreateDemoData(RoutingProblemData problem, API api) { - - LocationData locationData = TestHelper.createLocationWithCoordinates(Location.VEHICLE_START); - LocationData pickupLocation = TestHelper.createLocationWithCoordinates(Location.TASK_PICKUP); - LocationData deliveryLocation = TestHelper.createLocationWithCoordinates(Location.TASK_DELIVERY); - - ArrayList capacities = new ArrayList(); - capacities.add(new CapacityData("Weight", 100000)); - TimeWindowData twd = TestHelper.createTimeWindow(7, 20); - List tws = new ArrayList(); - //tws.add(twd); - VehicleUpdateRequest vehicleRequest = TestHelper.createVehicleUpdateRequest(UUID.randomUUID().toString()); - + try { api.navigate(ResponseData.class, problem.getLink("create-vehicle"), vehicleRequest); diff --git a/fi/cosky/sdk/tests/TestHelper.java b/fi/cosky/sdk/tests/TestHelper.java index 189c944..193e1c5 100644 --- a/fi/cosky/sdk/tests/TestHelper.java +++ b/fi/cosky/sdk/tests/TestHelper.java @@ -180,6 +180,7 @@ static List createListOfTasks(int howMany) { task.setName("testTask" + i); task.setRelocationType("None"); task.setActivityState("Active"); + task.setInfo("task " + i); tasks.add(task); } return tasks; @@ -197,13 +198,40 @@ static VehicleUpdateRequest createVehicleUpdateRequest(String name) { vehicleRequest.setVehicleSpeedProfile( SpeedProfile.Max80Kmh.toString() ); vehicleRequest.setVehicleSpeedFactor(0.7); vehicleRequest.setTimeWindows(timeWindows); + vehicleRequest.setInfo1("Info1"); + vehicleRequest.setVehicleType("Rekka"); return vehicleRequest; } + static VehicleUpdateRequest createVehicleUpdateRequestWithAddress(String name) { + ArrayList capacities = new ArrayList(); + capacities.add(new CapacityData("Weight", 100000)); + ArrayList timeWindows = new ArrayList(); + + timeWindows.add(createTimeWindow(7, 20)); + + LocationData startLocation = createLocationWithAddress(); + VehicleUpdateRequest vehicleRequest = new VehicleUpdateRequest(name, capacities, startLocation, startLocation); + vehicleRequest.setVehicleSpeedProfile( SpeedProfile.Max80Kmh.toString() ); + vehicleRequest.setVehicleSpeedFactor(0.7); + vehicleRequest.setTimeWindows(timeWindows); + vehicleRequest.setInfo1("Info1"); + return vehicleRequest; + + } + static List createListOfVehicles(int howMany) { + List vehicles = new ArrayList(); + + for (int i = 0; i < howMany; i++) { + vehicles.add(createVehicleUpdateRequest("vehicle"+i)); + } + return vehicles; + } + static DepotUpdateRequest createDepotUpdateRquest(String name) { LocationData location = new LocationData(); - location.setCoordinatesData(new CoordinateData( 0.0, 0.0, CoordinateSystem.Euclidian )); + location.setCoordinatesData(new CoordinateData( 62.231023, 25.698652, CoordinateSystem.WGS84 )); ArrayList capacities = new ArrayList(); capacities.add(new CapacityData("Weight", 1000)); @@ -219,13 +247,13 @@ static LocationData createLocationWithCoordinates(Location name) { CoordinateData coordinates = new CoordinateData(); switch (name){ case TASK_PICKUP: { - coordinates.setLatitude(62.244958); - coordinates.setLongitude(25.747143); + coordinates.setLatitude(62.281020); + coordinates.setLongitude(25.802570); break; } case TASK_DELIVERY: { - coordinates.setLatitude(62.244589); - coordinates.setLongitude(25.74892); + coordinates.setLatitude(62.290522); + coordinates.setLongitude(25.738774); break; } case VEHICLE_END: { @@ -233,16 +261,13 @@ static LocationData createLocationWithCoordinates(Location name) { coordinates.setLongitude(25.727949); break; } - case VEHICLE_START: - default: { - coordinates.setLatitude(62.247906); - coordinates.setLongitude(25.867395); - break; - } - - + case VEHICLE_START: { + coordinates.setLatitude(62.247906); + coordinates.setLongitude(25.867395); + break; + } } - coordinates.setSystem(CoordinateSystem.Euclidian); + coordinates.setSystem(CoordinateSystem.WGS84); LocationData data = new LocationData(); data.setCoordinatesData(coordinates); return data;