/* * Copyright (C) 2010-2012 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo FLOW. * * Akvo FLOW is free software: you can redistribute it and modify it under the terms of * the GNU Affero General Public License (AGPL) as published by the Free Software Foundation, * either version 3 of the License or any later version. * * Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License included below for more details. * * The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>. */ package org.waterforpeople.mapping.app.web; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import net.sf.jsr107cache.Cache; import net.sf.jsr107cache.CacheException; import net.sf.jsr107cache.CacheFactory; import net.sf.jsr107cache.CacheManager; import org.json.JSONObject; import org.waterforpeople.mapping.app.gwt.client.location.PlacemarkDto; import org.waterforpeople.mapping.app.web.dto.PlacemarkRestRequest; import org.waterforpeople.mapping.app.web.dto.PlacemarkRestResponse; import org.waterforpeople.mapping.dao.AccessPointDao; import org.waterforpeople.mapping.domain.AccessPoint; import org.waterforpeople.mapping.domain.AccessPoint.AccessPointType; import org.waterforpeople.mapping.domain.AccessPoint.Status; import com.gallatinsystems.common.util.PropertyUtil; import com.gallatinsystems.framework.rest.AbstractRestApiServlet; import com.gallatinsystems.framework.rest.RestRequest; import com.gallatinsystems.framework.rest.RestResponse; import com.gallatinsystems.standards.domain.LOSScoreToStatusMapping; import com.gallatinsystems.standards.domain.Standard.StandardType; import com.gallatinsystems.surveyal.dao.SurveyedLocaleDao; import com.gallatinsystems.surveyal.domain.SurveyedLocale; import com.google.appengine.api.memcache.MemcacheService; import com.google.appengine.api.memcache.jsr107cache.GCacheFactory; public class PlacemarkServlet extends AbstractRestApiServlet { private static final long serialVersionUID = -9031594440737716966L; private static final Logger log = Logger.getLogger(PlacemarkServlet.class .getName()); private static final String AP_DOMAIN = "accessPoint"; private static final int CACHE_EXPIRY_DEFAULT = 3600; private static final String CACHE_EXP_PROP = "cacheExpirySeconds"; private KMLGenerator kmlGen = new KMLGenerator(); private Cache cache; private AccessPointDao apDao; private SurveyedLocaleDao localeDao; private int cacheExpirySec = CACHE_EXPIRY_DEFAULT; @SuppressWarnings({ "unchecked", "rawtypes" }) public PlacemarkServlet() { super(); apDao = new AccessPointDao(); localeDao = new SurveyedLocaleDao(); CacheFactory cacheFactory; try { cacheFactory = CacheManager.getInstance().getCacheFactory(); Map configMap = new HashMap(); String cacheExpString = PropertyUtil.getProperty(CACHE_EXP_PROP); if (cacheExpString != null) { try { cacheExpirySec = Integer.parseInt(cacheExpString); } catch (Exception e) { // no-op } } if (cacheExpirySec <= 0) { cacheExpirySec = CACHE_EXPIRY_DEFAULT; } configMap.put(GCacheFactory.EXPIRATION_DELTA, cacheExpirySec); configMap.put(MemcacheService.SetPolicy.SET_ALWAYS, true); cache = cacheFactory.createCache(configMap); } catch (CacheException e) { log.log(Level.SEVERE, "Could not initialize cache", e); } } @Override protected RestRequest convertRequest() throws Exception { HttpServletRequest req = getRequest(); RestRequest restRequest = new PlacemarkRestRequest(); restRequest.populateFromHttpRequest(req); return restRequest; } @Override protected RestResponse handleRequest(RestRequest req) throws Exception { PlacemarkRestRequest piReq = (PlacemarkRestRequest) req; if (cache != null && !piReq.getIgnoreCache()) { PlacemarkRestResponse cachedResponse = null; try { log.log(Level.INFO, "Checking Cache for: " + piReq.getCacheKey()); cachedResponse = (PlacemarkRestResponse) cache.get(piReq .getCacheKey()); } catch (Throwable t) { log.log(Level.WARNING, "Could not look up data in cache", t); } if (cachedResponse != null) { return cachedResponse; } } int desiredResults = 20; if (piReq.getDesiredResults() > 20) { if (piReq.getDesiredResults() > 500) { desiredResults = 500; } else { desiredResults = piReq.getDesiredResults(); } } String display = null; if (piReq.getDisplay() != null) { display = piReq.getDisplay(); } PlacemarkRestResponse response = null; // if we had a cache miss (or the cache is not available), then hit the // datastore and cache the result StandardType standardType = piReq.getStandardType(); if (piReq.getDomain() == null || AP_DOMAIN.equalsIgnoreCase(piReq.getDomain())) { if (piReq.getAction() != null && PlacemarkRestRequest.GET_AP_DETAILS_ACTION.equals(piReq .getAction())) { List<AccessPoint> apList = new ArrayList<AccessPoint>(); if (piReq.getCommunityCode() != null && piReq.getCommunityCode().trim().length() > 0) { AccessPoint ap = (AccessPoint) apDao.findAccessPoint( piReq.getCommunityCode(), piReq.getPointType()); if (ap != null) apList.add(ap); else log.log(Level.SEVERE, "PlacemarkServlet.handleRequest getAPDetails didn't find AP for CommunityCode : " + piReq.getCommunityCode() + " piReq.getPointType: " + piReq.getPointTypeString()); } response = (PlacemarkRestResponse) convertToResponse(apList, true, null, null, piReq.getDisplay(), standardType); } else { // ListPlacemarks Action if (piReq.getAction() == null) { if (piReq.getOrg() != null) { List<AccessPoint> results = apDao.searchAccessPoints( piReq.getCountry(), null, null, null, null, null, null, null, piReq.getOrg(), "collectionDate", "asc", desiredResults, piReq.getCursor()); response = (PlacemarkRestResponse) convertToResponse( results, piReq.getNeedDetailsFlag(), AccessPointDao.getCursor(results), piReq.getCursor(), display, standardType); } else if (piReq.getSubLevel() != null) { List<AccessPoint> results = apDao.listBySubLevel( piReq.getCountry(), piReq.getSubLevel(), piReq.getSubLevelValue(), piReq.getCursor(), AccessPointType.WATER_POINT, desiredResults); response = (PlacemarkRestResponse) convertToResponse( results, piReq.getNeedDetailsFlag(), AccessPointDao.getCursor(results), piReq.getCursor(), display, standardType); } else { List<AccessPoint> results = apDao.searchAccessPoints( piReq.getCountry(), null, null, null, null, null, null, null, null, null, desiredResults, piReq.getCursor()); if (piReq.getDisplay() != null) { display = piReq.getDisplay(); } response = (PlacemarkRestResponse) convertToResponse( results, piReq.getNeedDetailsFlag(), AccessPointDao.getCursor(results), piReq.getCursor(), display, standardType); } } else if (piReq.getAction().equals( PlacemarkRestRequest.LIST_BOUNDING_BOX_ACTION) && piReq.getLat1() != null) { List<AccessPoint> results = apDao .listAccessPointsByBoundingBox( piReq.getPointType(), piReq.getLat1(), piReq.getLat2(), piReq.getLong1(), piReq.getLong2(), piReq.getCursor(), desiredResults); response = (PlacemarkRestResponse) convertToResponse( results, piReq.getNeedDetailsFlag(), AccessPointDao.getCursor(results), piReq.getCursor(), piReq.getDisplay(), standardType); } } } else { // if we're here, we're operating on SurveyedLocale, not AccessPoint if (piReq.getAction() != null && PlacemarkRestRequest.GET_AP_DETAILS_ACTION.equals(piReq .getAction())) { List<SurveyedLocale> localeList = localeDao.listLocalesByCode( piReq.getCommunityCode(), true); response = (PlacemarkRestResponse) convertLocaleToResponse( localeList, true, null, null, piReq.getDisplay()); } else if (PlacemarkRestRequest.LIST_BOUNDING_BOX_ACTION .equals(piReq.getAction()) && piReq.getLat1() != null) { List<SurveyedLocale> results = localeDao .listLocalesByCoordinates(piReq.getPointTypeString(), piReq.getLat1(), piReq.getLat2(), piReq.getLong1(), piReq.getLong2(), piReq.getCursor(), desiredResults); response = (PlacemarkRestResponse) convertLocaleToResponse( results, piReq.getNeedDetailsFlag(), SurveyedLocaleDao.getCursor(results), piReq.getCursor(), piReq.getDisplay()); } else { // ListPlacemarks Action List<SurveyedLocale> results = localeDao.listBySubLevel( piReq.getCountry(), null, null, piReq.getPointTypeString(), null, piReq.getCursor(), desiredResults); response = (PlacemarkRestResponse) convertLocaleToResponse( results, piReq.getNeedDetailsFlag(), SurveyedLocaleDao.getCursor(results), piReq.getCursor(), display); } } if (response != null && cache != null) { try { cache.put(piReq.getCacheKey(), response); } catch (Throwable t) { log.log(Level.WARNING, "Could not cache results", t); } } return response; } private RestResponse convertToResponse(List<AccessPoint> apList, Boolean needDetailsFlag, String cursor, String oldCursor, String display, StandardType standardType) { PlacemarkRestResponse resp = new PlacemarkRestResponse(); if (needDetailsFlag == null) needDetailsFlag = true; if (apList != null) { List<PlacemarkDto> dtoList = new ArrayList<PlacemarkDto>(); for (AccessPoint ap : apList) { if (!ap.getPointType().equals(AccessPointType.SANITATION_POINT)) { PlacemarkDto pdto = marshallDomainToDto(ap, needDetailsFlag, display, standardType); if (pdto != null) dtoList.add(pdto); } resp.setPlacemarks(dtoList); } } if (cursor != null) { if (oldCursor == null || !cursor.equals(oldCursor)) { resp.setCursor(cursor); } } else { resp.setCursor(null); } return resp; } private RestResponse convertLocaleToResponse( List<SurveyedLocale> localeList, Boolean needDetailsFlag, String cursor, String oldCursor, String display) { PlacemarkRestResponse resp = new PlacemarkRestResponse(); if (needDetailsFlag == null) { needDetailsFlag = true; } if (localeList != null) { List<PlacemarkDto> dtoList = new ArrayList<PlacemarkDto>(); for (SurveyedLocale ap : localeList) { dtoList.add(marshallDomainToDto(ap, needDetailsFlag, display)); } resp.setPlacemarks(dtoList); } if (cursor != null) { if (oldCursor == null || !cursor.equals(oldCursor)) { resp.setCursor(cursor); } } else { resp.setCursor(null); } return resp; } private PlacemarkDto marshallDomainToDto(SurveyedLocale ap, Boolean needDetailsFlag, String display) { PlacemarkDto pdto = new PlacemarkDto(); pdto.setPinStyle(KMLGenerator.encodePinStyle(ap.getLocaleType(), ap.getCurrentStatus())); pdto.setIconUrl(KMLGenerator.getMarkerImageUrl(ap.getLocaleType(), ap.getCurrentStatus())); if (ap.getLocaleType() == null) { pdto.setMarkType(AccessPointType.WATER_POINT.toString()); } else { pdto.setMarkType(ap.getLocaleType()); } pdto.setLatitude(ap.getLatitude()); pdto.setLongitude(ap.getLongitude()); pdto.setCommunityCode(ap.getIdentifier()); pdto.setCollectionDate(ap.getLastUpdateDateTime()); if (needDetailsFlag) { String placemarkString = null; try { placemarkString = kmlGen.bindPlacemark(ap, "localePlacemarkExternal.vm", display); pdto.setPlacemarkContents(placemarkString); } catch (Exception e) { log.log(Level.SEVERE, "Could not bind placemarks", e); } } return pdto; } private PlacemarkDto marshallDomainToDto(AccessPoint ap, Boolean needDetailsFlag, String display, StandardType standardType) { PlacemarkDto pdto = new PlacemarkDto(); if (standardType != null) { KMLGenerator kmlGen = new KMLGenerator(); LOSScoreToStatusMapping losItem = kmlGen.encodePinStyle( ap.getKey(), standardType); pdto.setPinStyle(losItem.getIconStyle()); pdto.setIconUrl(losItem.getIconLargeUrl()); } else { pdto.setPinStyle(KMLGenerator.encodePinStyle(ap.getPointType(), ap.getPointStatus())); pdto.setIconUrl(getUrlFromStatus(ap.getPointStatus(), ap.getPointType())); } pdto.setLatitude(ap.getLatitude()); pdto.setLongitude(ap.getLongitude()); pdto.setCommunityCode(ap.getCommunityCode()); pdto.setMarkType(ap.getPointType().toString()); pdto.setCollectionDate(ap.getCollectionDate()); if (needDetailsFlag) { String placemarkString = null; try { placemarkString = kmlGen.bindPlacemark(ap, "placemarkExternalMap.vm", display, standardType); pdto.setPlacemarkContents(placemarkString); } catch (Exception e) { log.log(Level.SEVERE, "Could not bind placemarks", e); } } return pdto; } private String getUrlFromStatus(Status status, AccessPoint.AccessPointType pointType) { if (pointType .equals(AccessPoint.AccessPointType.WATER_POINT) && status == null) { return KMLGenerator.WATER_POINT_FUNCTIONING_BLACK_ICON_URL; } if (AccessPointType.WATER_POINT.equals(pointType)) { if (status != null) { if (status.equals(AccessPoint.Status.FUNCTIONING_HIGH)) { return KMLGenerator.WATER_POINT_FUNCTIONING_GREEN_ICON_URL; } else if (status.equals(AccessPoint.Status.FUNCTIONING_OK) || status .equals(AccessPoint.Status.FUNCTIONING_WITH_PROBLEMS)) { return KMLGenerator.WATER_POINT_FUNCTIONING_YELLOW_ICON_URL; } else if (status.equals(AccessPoint.Status.BROKEN_DOWN)) { return KMLGenerator.WATER_POINT_FUNCTIONING_RED_ICON_URL; } else if (status.equals(AccessPoint.Status.NO_IMPROVED_SYSTEM)) { return KMLGenerator.WATER_POINT_FUNCTIONING_BLACK_ICON_URL; } else { return KMLGenerator.WATER_POINT_FUNCTIONING_BLACK_ICON_URL; } } else { return KMLGenerator.WATER_POINT_FUNCTIONING_BLACK_ICON_URL; } } else if (AccessPointType.PUBLIC_INSTITUTION.equals(pointType)) { if (status != null) { if (status.equals(AccessPoint.Status.FUNCTIONING_HIGH)) { return KMLGenerator.PUBLIC_INSTITUTION_FUNCTIONING_GREEN_ICON_URL; } else if (status.equals(AccessPoint.Status.FUNCTIONING_OK) || status .equals(AccessPoint.Status.FUNCTIONING_WITH_PROBLEMS)) { return KMLGenerator.PUBLIC_INSTITUTION_FUNCTIONING_YELLOW_ICON_URL; } else if (status.equals(AccessPoint.Status.BROKEN_DOWN)) { return KMLGenerator.PUBLIC_INSTITUTION_FUNCTIONING_RED_ICON_URL; } else if (status.equals(AccessPoint.Status.NO_IMPROVED_SYSTEM)) { return KMLGenerator.PUBLIC_INSTITUTION_FUNCTIONING_BLACK_ICON_URL; } else { return KMLGenerator.PUBLIC_INSTITUTION_FUNCTIONING_BLACK_ICON_URL; } } else { return KMLGenerator.PUBLIC_INSTITUTION_FUNCTIONING_BLACK_ICON_URL; } } else if (AccessPointType.SCHOOL.equals(pointType)) { if (status != null) { if (status.equals(AccessPoint.Status.FUNCTIONING_HIGH)) { return KMLGenerator.SCHOOL_INSTITUTION_FUNCTIONING_GREEN_ICON_URL; } else if (status.equals(AccessPoint.Status.FUNCTIONING_OK) || status .equals(AccessPoint.Status.FUNCTIONING_WITH_PROBLEMS)) { return KMLGenerator.SCHOOL_INSTITUTION_FUNCTIONING_YELLOW_ICON_URL; } else if (status.equals(AccessPoint.Status.BROKEN_DOWN)) { return KMLGenerator.SCHOOL_INSTITUTION_FUNCTIONING_RED_ICON_URL; } else if (status.equals(AccessPoint.Status.NO_IMPROVED_SYSTEM)) { return KMLGenerator.SCHOOL_INSTITUTION_FUNCTIONING_BLACK_ICON_URL; } else { return KMLGenerator.SCHOOL_INSTITUTION_FUNCTIONING_BLACK_ICON_URL; } } else { return KMLGenerator.SCHOOL_INSTITUTION_FUNCTIONING_BLACK_ICON_URL; } } return null; } @Override protected void writeOkResponse(RestResponse resp) throws Exception { getResponse().setStatus(200); PlacemarkRestResponse piResp = (PlacemarkRestResponse) resp; JSONObject result = new JSONObject(piResp); getResponse().getWriter().println(result.toString()); } }