/* Copyright (c) 2010, skobbler GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.openstreetmap.josm.plugins.mapdust.service.connector; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openstreetmap.josm.plugins.mapdust.service.connector.response.MapdustGetBugResponse; import org.openstreetmap.josm.plugins.mapdust.service.connector.response.MapdustGetBugsResponse; import org.openstreetmap.josm.plugins.mapdust.service.connector.response.MapdustPostResponse; import org.openstreetmap.josm.plugins.mapdust.service.value.BoundingBox; import org.openstreetmap.josm.plugins.mapdust.service.value.MapdustBug; import org.openstreetmap.josm.plugins.mapdust.service.value.MapdustBugFilter; import org.openstreetmap.josm.plugins.mapdust.service.value.MapdustComment; import org.openstreetmap.josm.plugins.mapdust.service.value.MapdustResponseStatusCode; import org.openstreetmap.josm.plugins.mapdust.service.value.Paging; import org.openstreetmap.josm.plugins.mapdust.util.Configuration; import org.openstreetmap.josm.plugins.mapdust.util.http.HttpConnector; import org.openstreetmap.josm.plugins.mapdust.util.http.HttpResponse; import org.openstreetmap.josm.plugins.mapdust.util.retry.RetrySetup; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; /** * The <code>MapdustConnector</code> object. Connects to the Mapdust service, * and executes the following Mapdust service methods: getBug, getBugs, addBug, * commentBug and changeBugStatus. * * @author Bea */ public class MapdustConnector { /** The <code>HttpConnector</code> object */ private final HttpConnector httpConnector; private Gson gson; /** * Builds a <code>MapdustConnector</code> object with the default settings. */ public MapdustConnector() { httpConnector = new HttpConnector(RetrySetup.DEFAULT); gson = buildGson(); } private Gson buildGson() { GsonBuilder builder = new GsonBuilder(); builder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); return builder.create(); } /** * Searches for the OSM MapDust bugs in the given bounding box. The method * executes the 'getBugs' MapDust service method, parses the obtained * response object and return a <code>MapdustGetBugsResponse</code> object * containing the pagination information and the array of bugs. In the case * if the response code is not 200,201 or 204, a corresponding exception * will be thrown. * * @param bBox The bounding box where the bugs are searched. * @param filter The MapDust bug filter. The bugs can be filtered based on * the status, type and description. This parameter is not required. * @return A <code>MapdustGetBugsResponse</code> object, containing the * pagination information and an array of <code>MapdustBugContent</code> * object. * @throws MapdustConnectorException In the case of an error. */ public MapdustGetBugsResponse getBugs(BoundingBox bBox, MapdustBugFilter filter) throws MapdustConnectorException { /* execute GET method and get the response */ HttpResponse httpResponse = null; try { httpResponse = executeGetBugs(bBox, filter); } catch (Exception e) { throw new MapdustConnectorException(e.getMessage(), e); } /* parse HttpResponse */ MapdustGetBugsResponse result = null; try { /* verify status codes */ handleStatusCode(httpResponse); result = parseResponse(httpResponse.getContent(), MapdustGetBugsResponse.class); } catch (MapdustConnectorException e) { throw new MapdustConnectorException(e.getMessage(), e); } return result; } /** * Returns the OSM bug with the given id. If the <code>Paging</code> object * is set, then the comments of the bug will be paginated. The method * executes the 'getBug' MapDust service method, parses the obtained * response object and return a <code>MapdustGetBugResponse</code> object. * In the case if the response code is not 2 * * @param id The id of the object * @param paging The <code>Paging</code> object * @return A <code>MapdustGetBugResponse</code> object. * * @throws MapdustConnectorException In the case of an error. */ public MapdustGetBugResponse getBug(Long id, Paging paging) throws MapdustConnectorException { HttpResponse httpResponse = null; try { httpResponse = executeGetBug(id, paging); } catch (Exception e) { throw new MapdustConnectorException(e.getMessage(), e); } /* parse result */ MapdustGetBugResponse result = null; try { handleStatusCode(httpResponse); result = parseResponse(httpResponse.getContent(), MapdustGetBugResponse.class); } catch (MapdustConnectorException e) { throw new MapdustConnectorException(e.getMessage(), e); } return result; } /** * Creates a new OSM bug with the specified arguments. The method executes * the 'addBug' MapDust service method, parses the obtained response object * and return a <code>MapdustPostResponse</code> object containing the id of * the created comment. In the case if the response code is not 200,201 or * 204, a corresponding exception will be thrown. * * @param bug A <code>MapdustBug</code> object * @return A <code>MapdustPostResponse</code> object which contains the id * of the created object. * * @throws MapdustConnectorException In the case of an error */ public MapdustPostResponse addBug(MapdustBug bug) throws MapdustConnectorException { HttpResponse httpResponse = null; try { httpResponse = executeAddBug(bug); } catch (Exception e) { throw new MapdustConnectorException(e.getMessage(), e); } /* parse result */ MapdustPostResponse result = null; try { handleStatusCode(httpResponse); result = parseResponse(httpResponse.getContent(), MapdustPostResponse.class); } catch (MapdustConnectorException e) { throw new MapdustConnectorException(e.getMessage(), e); } return result; } /** * Creates a new comment for the given bug. The method executes the * 'commentBug' MapDust service method, parses the obtained response object * and return a <code>MapdustPostResponse</code> object containing the id of * the created comment. In the case if the response code is not 200,201 or * 204, a corresponding exception will be thrown. * * @param comment A <code>MapdustComment</code> object * @return A <code>MapdustPostResponse</code> object which contains the id * of the created object. * @throws MapdustConnectorException In the case of an error */ public MapdustPostResponse commentBug(MapdustComment comment) throws MapdustConnectorException { HttpResponse httpResponse = null; try { httpResponse = executeCommentBug(comment); } catch (Exception e) { throw new MapdustConnectorException(e.getMessage(), e); } /* parse result */ MapdustPostResponse result = null; try { handleStatusCode(httpResponse); result = parseResponse(httpResponse.getContent(), MapdustPostResponse.class); } catch (MapdustConnectorException e) { throw new MapdustConnectorException(e.getMessage(), e); } return result; } /** * Changes the status of a given bug. The method executes the * 'changeBugStatus' MapDust service method, parses the obtained response * object and return a <code>MapdustPostResponse</code> object containing * the id of the created comment. In the case if the response code is not * 200,201 or 204, a corresponding exception will be thrown. * * @param statusId The new value for the status. Possible values are: 1, 2 * or 3. * @param comment A <code>MapdustComment</code> object * @return A <code>MapdustPostResponse</code> object which contains the id * of the created object. * @throws MapdustConnectorException In the case of an error */ public MapdustPostResponse changeBugStatus(Integer statusId, MapdustComment comment) throws MapdustConnectorException { HttpResponse httpResponse = null; try { httpResponse = executeChangeBugStatus(statusId, comment); } catch (Exception e) { throw new MapdustConnectorException(e.getMessage(), e); } /* parse result */ MapdustPostResponse result = null; try { handleStatusCode(httpResponse); result = parseResponse(httpResponse.getContent(), MapdustPostResponse.class); } catch (MapdustConnectorException e) { throw new MapdustConnectorException(e.getMessage(), e); } return result; } /** * Executes the 'getBugs' MapDust service method. * * @param bBox The bounding box where the bugs are searched. This parameter * it is a required parameter. * @param filter The MapDust bug filter. The bugs can be filtered based on * the status, type and description. This parameter is not required. * @return A <code>HttpResponse</code> object containing the JSON response. * @throws MalformedURLException In the case if the format of the URL is * invalid * @throws IOException In the case of an IO error */ private HttpResponse executeGetBugs(BoundingBox bBox, MapdustBugFilter filter) throws MalformedURLException, IOException { HttpResponse httpResponse = null; String mapdustUri = Configuration.getInstance().getMapdustUrl(); String mapdustApiKey = Configuration.getInstance().getMapdustKey(); String urlString = null; if (mapdustUri != null && mapdustApiKey != null) { urlString = mapdustUri; urlString += "/getBugs?"; urlString += "key=" + mapdustApiKey; urlString += "&bbox=" + bBox.getMinLon(); urlString += "," + bBox.getMinLat(); urlString += "," + bBox.getMaxLon(); urlString += "," + bBox.getMaxLat(); if (filter != null) { if (filter.getStatuses() != null && filter.getStatuses().size() > 0) { String paramStatus = buildParameter(filter.getStatuses()); if (!paramStatus.isEmpty()) { urlString += "&fs=" + paramStatus; } } if (filter.getTypes() != null && filter.getTypes().size() > 0) { String paramTypes = buildParameter(filter.getTypes()); if (!paramTypes.isEmpty()) { urlString += "&ft=" + paramTypes; } } if (filter.getDescr() != null && filter.getDescr()) { urlString += "&idd=" + "0"; } if (filter.getMinRelevance() != null) { int min = filter.getMinRelevance().getRange().getLowerValue(); urlString += "&minr=" + min; } if (filter.getMaxRelevance() != null) { int max = filter.getMaxRelevance().getRange().getUpperValue(); urlString += "&maxr=" + max; } } } URL url = null; if (urlString != null) { url = new URL(urlString); httpResponse = httpConnector.executeGET(url); } return httpResponse; } /** * Executes the 'getBug' MapDust service method. * * @param id The id of the object * @param paging The <code>Paging</code> object * @return A <code>HttpResponse</code> containing the JSON response of the * Mapdust method. * * @throws MalformedURLException In the case if the format of the URL is * invalid * @throws IOException In the case of an IO error */ private HttpResponse executeGetBug(Long id, Paging paging) throws MalformedURLException, IOException { HttpResponse httpResponse = null; String mapdustUri = Configuration.getInstance().getMapdustUrl(); String mapdustApiKey = Configuration.getInstance().getMapdustKey(); String urlString = null; if (mapdustUri != null && mapdustApiKey != null) { urlString = mapdustUri; urlString += "/getBug?"; urlString += "key=" + mapdustApiKey; if (id != null) { urlString += "&id=" + id; } if (paging != null && paging.getItems() != null && paging.getPage() != null) { urlString += "&items=" + paging.getItems(); urlString += "&p=" + paging.getPage(); } } URL url = null; if (urlString != null) { url = new URL(urlString); httpResponse = httpConnector.executeGET(url); } return httpResponse; } /** * Executes the 'addBug' MapDust service method. * * @param bug A <code>MapdustBug</code> object * @return A <code>HttpResponse</code> containing the JSON response of the * MapDust method. * * @throws MalformedURLException In the case if the format of the URL is * invalid * @throws IOException In the case of an IO error */ private HttpResponse executeAddBug(MapdustBug bug) throws MalformedURLException, IOException { HttpResponse httpResponse = null; String mapdustUri = Configuration.getInstance().getMapdustUrl(); String mapdustApiKey = Configuration.getInstance().getMapdustKey(); String urlString = null; Map<String, String> requestParameters = new HashMap<>(); if (mapdustUri != null && mapdustApiKey != null) { urlString = mapdustUri; urlString += "/addBug"; requestParameters.put("key", mapdustApiKey); String coordinatesStr = "" + bug.getLatLon().getX(); coordinatesStr += "," + bug.getLatLon().getY(); requestParameters.put("coordinates", coordinatesStr); requestParameters.put("type", bug.getType().getKey()); requestParameters.put("description", bug.getDescription()); requestParameters.put("nickname", bug.getNickname()); } URL url = null; if (urlString != null) { url = new URL(urlString); httpResponse = httpConnector.executePOST(url, null, requestParameters); } return httpResponse; } /** * Executes the 'commentBug' MapDust service method. * * @param comment The <code>MapdustComment</code> object * @return A <code>HttpResponse</code> containing the JSON response of the * MapDust method. * @throws MalformedURLException In the case if the format of the URL is * invalid * @throws IOException In the case of an IO error */ private HttpResponse executeCommentBug(MapdustComment comment) throws MalformedURLException, IOException { HttpResponse httpResponse = null; String mapdustUri = Configuration.getInstance().getMapdustUrl(); String mapdustApiKey = Configuration.getInstance().getMapdustKey(); String urlString = null; Map<String, String> requestParameters = new HashMap<>(); if (mapdustUri != null && mapdustApiKey != null) { urlString = mapdustUri; urlString += "/commentBug"; requestParameters.put("key", mapdustApiKey); requestParameters.put("id", comment.getBugId().toString()); requestParameters.put("comment", comment.getCommentText()); requestParameters.put("nickname", comment.getNickname()); } URL url = null; if (urlString != null) { url = new URL(urlString); httpResponse = httpConnector.executePOST(url, null, requestParameters); } return httpResponse; } /** * Executes the 'changeBugStatus' MapDust service method. * * @param statusId The id of the status. * @param comment A <code>MapdustComment</code> object. * @return A <code>HttpResponse</code> containing the JSON response of the * MapDust method. * * @throws MalformedURLException In the case if the format of the URL is * invalid * @throws IOException In the case of an IO error */ private HttpResponse executeChangeBugStatus(Integer statusId, MapdustComment comment) throws MalformedURLException, IOException { HttpResponse httpResponse = null; String mapdustUri = Configuration.getInstance().getMapdustUrl(); String mapdustApiKey = Configuration.getInstance().getMapdustKey(); String urlString = null; Map<String, String> requestParameters = new HashMap<>(); if (mapdustUri != null && mapdustApiKey != null) { urlString = mapdustUri; urlString += "/changeBugStatus"; requestParameters.put("key", mapdustApiKey); requestParameters.put("id", comment.getBugId().toString()); requestParameters.put("status", statusId.toString()); requestParameters.put("comment", comment.getCommentText()); requestParameters.put("nickname", comment.getNickname()); } URL url = null; if (urlString != null) { url = new URL(urlString); httpResponse = httpConnector.executePOST(url, null, requestParameters); } return httpResponse; } /** * Builds a string containing the elements of the given list. The elements * will be separated by a comma. If the list does not contains any element * the returned result will be an empty string. * * @param list The list of objects. * @return a string */ private String buildParameter(List<? extends Object> list) { StringBuffer sb = new StringBuffer(); for (Object obj : list) { if (obj != null) { sb.append(obj).append(","); } } return sb.substring(0, sb.length() - 1); } /** * Handles the response codes of the given <code>HttpResponse</code> object. * If the response code is 200, 201 or 204, the method returns without any * exception. Otherwise a <code>MapdustConnectorException</code> will be * thrown with an appropriate message. * * @param httpResponse The <code>HttpResponse</code> method. * @throws MapdustConnectorException In the case if the status code is not * 200, 201 or 204. */ private void handleStatusCode(HttpResponse httpResponse) throws MapdustConnectorException { String error = ""; Integer statusCode = httpResponse.getStatusCode(); String statusMessage = httpResponse.getStatusMessage(); if (statusCode.equals(MapdustResponseStatusCode.Status200 .getStatusCode()) || statusCode.equals(MapdustResponseStatusCode.Status201 .getStatusCode()) || statusCode.equals(MapdustResponseStatusCode.Status204 .getStatusCode())) { // no error return; } switch (statusCode) { case 400: error = statusMessage + " "; error += MapdustResponseStatusCode.Status400.getDescription(); throw new MapdustConnectorException(error); case 401: error = statusMessage + " "; error += MapdustResponseStatusCode.Status401.getDescription(); throw new MapdustConnectorException(error); case 403: error = statusMessage + " "; error += MapdustResponseStatusCode.Status403.getDescription(); throw new MapdustConnectorException(error); case 404: error = statusMessage + " "; error += MapdustResponseStatusCode.Status404.getDescription(); throw new MapdustConnectorException(error); case 405: error = statusMessage + " "; error += MapdustResponseStatusCode.Status405.getDescription(); throw new MapdustConnectorException(error); case 500: error = statusMessage + " "; error += MapdustResponseStatusCode.Status500.getDescription(); throw new MapdustConnectorException(error); case 601: error = statusMessage + " "; error += MapdustResponseStatusCode.Status601.getDescription(); throw new MapdustConnectorException(error); case 602: error = statusMessage + " "; error += MapdustResponseStatusCode.Status602.getDescription(); throw new MapdustConnectorException(error); default: throw new MapdustConnectorException( MapdustResponseStatusCode.Status.getDescription()); } } private <T> T parseResponse(String httpResponse, Class<T> responseType) throws MapdustConnectorException { T result; try { result = gson.fromJson(httpResponse, responseType); } catch (JsonSyntaxException e) { throw new MapdustConnectorException(e.getMessage(), e); } return result; } }