Java tutorial
/** * Copyright (c) 2016 Modus Operandi, Inc. * * This is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or any later version. * * This program 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 Lesser General Public License for more * details. A copy of the GNU Lesser General Public License is distributed along * with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package com.modusoperandi.dragonfly.widgets.map; import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.io.GeohashUtils; import com.spatial4j.core.shape.Point; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.InetAddress; import java.net.URLDecoder; import java.net.URLEncoder; import java.net.UnknownHostException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringEscapeUtils; import org.apache.wicket.Component; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptHeaderItem; import org.apache.wicket.markup.head.OnDomReadyHeaderItem; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.request.IRequestParameters; import org.apache.wicket.request.Url; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.util.time.Time; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import static org.elasticsearch.search.aggregations.AggregationBuilders.geohashGrid; import org.elasticsearch.search.aggregations.bucket.geogrid.GeoHashGrid; /** * The Class MapWidgetPage - renders the interactive map */ public class MapWidgetPage extends WebPage { public class HandleOnZoomLeafletCallbackAjaxBehavior extends AbstractDefaultAjaxBehavior { private static final long serialVersionUID = 1L; @Override public void renderHead(final Component component, final IHeaderResponse response) { super.renderHead(component, response); final StringBuffer callbackJs = new StringBuffer(); callbackJs.append("handleZoomCallbackUrl = '").append(this.getCallbackUrl()).append("';"); response.render(OnDomReadyHeaderItem.forScript(callbackJs.toString())); } @Override protected void respond(final AjaxRequestTarget target) { final IRequestParameters parameters = getRequest().getRequestParameters(); try { zoomLevel = Integer.parseInt(parameters.getParameterValue("zoom").toString("3")); } catch (final NumberFormatException e) { zoomLevel = 3; } zoomView = zoomLevel; // Only use square geohashes if (zoomLevel < 6) { zoomLevel = 3; } else { zoomLevel = 7; } try { south = Double.parseDouble(parameters.getParameterValue("south").toString("-90")); north = Double.parseDouble(parameters.getParameterValue("north").toString("90")); west = Double.parseDouble(parameters.getParameterValue("west").toString("-180")); east = Double.parseDouble(parameters.getParameterValue("east").toString("180")); } catch (final NumberFormatException e) { south = -90; north = 90; west = -180; east = 180; } center = parameters.getParameterValue("center").toString(); refreshDataOnMap(heatMapSettingsPanel.getConfiguration(), target); } } private class AddAjaxButton extends AjaxButton { private static final long serialVersionUID = 1L; public AddAjaxButton() { super("addButton"); } @Override protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { addWindow.show(target); } } private class PermalinkAjaxButton extends AjaxButton { private static final long serialVersionUID = 1L; public PermalinkAjaxButton() { super("permalinkButton"); } @Override protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { informationPanel.setInformation("Permalink", generatePermalink(heatMapSettingsPanel.getConfiguration()), target); informationWindow.show(target); } } private class RefreshAjaxButton extends AjaxButton { private static final long serialVersionUID = 1L; public RefreshAjaxButton() { super("refreshButton"); } @Override protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) { refreshDataOnMap(heatMapSettingsPanel.getConfiguration(), target); } } private transient static Client esClient = null; private static final Logger LOGGER = Logger.getLogger(MapWidgetPage.class.getName()); private static final int MAX_GEOHASH_GRID = 20000; private static final int MAX_HITS_TO_SHOW = 2000; private static final int MAX_POPUP_ROW_LENGTH = 50; private static final int ONE_DAY_IN_SECONDS = 86400; private static final long serialVersionUID = 1L; public static void onDestroy() { if (esClient != null) { esClient.close(); esClient = null; } } private static void addGeojsonToMap(final AjaxRequestTarget target, final String indexName, final String geojsonFieldName) { final StringBuffer js = new StringBuffer(); final SearchRequestBuilder search = getEsClient().prepareSearch(indexName); search.setSearchType(SearchType.DFS_QUERY_THEN_FETCH); search.setQuery(QueryBuilders.matchAllQuery()); search.setSize(MAX_HITS_TO_SHOW); final SearchResponse results = search.execute().actionGet(); for (final SearchHit hit : results.getHits()) { final String valueStr = (String) hit.getSource().get(geojsonFieldName); if (valueStr != null) { target.appendJavaScript("geoJsonLayer.clearLayers();"); js.setLength(0); js.append("geoJsonLayer.addData("); js.append(valueStr); js.append(");"); target.appendJavaScript(js.toString()); } } } private static void addWmsToMap(final AjaxRequestTarget target, final String tileUrl, final String layers, final String format, final String attribution, final String name) { final StringBuffer tileParams = new StringBuffer(); tileParams.append("{"); tileParams.append("layers: '").append(layers).append("',"); tileParams.append("format: '").append(format).append("',"); tileParams.append("attribution: '").append(attribution).append("',"); tileParams.append("transparent: true"); tileParams.append("}"); final StringBuffer js = new StringBuffer(); js.append("var wms = L.tileLayer.wms('").append(tileUrl).append("',").append(tileParams.toString()) .append(");"); js.append("controls.addOverlay(wms,'").append(name).append("');"); js.append("wms.addTo(map);"); target.appendJavaScript(wrapAjaxJs("addWmsToMap", js.toString())); } /** * Retrieves the Elasticsearch index mapping for the specified index * * @param indexName * the index name * @return the index mapping */ private static ImmutableOpenMap<String, MappingMetaData> esGetMappings(final String indexName) { final ClusterStateResponse clusterStateResponse = getEsClient().admin().cluster().prepareState().execute() .actionGet(); return clusterStateResponse.getState().getMetaData().index(indexName).getMappings(); } private static void generateHeatmapJs(final SearchResponse results, final StringBuffer heatmapJs) { heatmapJs.append("if(!(typeof heatmapLayer === 'undefined') && (heatmapLayer != null)) {"); heatmapJs.append("heatmapLayer.setData({data:["); long min = Long.MAX_VALUE; long max = 2; // The minimum max boolean firstTime = true; final GeoHashGrid geoGrid = results.getAggregations().get("geohashgrid"); for (final GeoHashGrid.Bucket cell : geoGrid.getBuckets()) { final String geohash = cell.getKeyAsString(); final long bucketCount = cell.getDocCount(); final Point hashCenter = GeohashUtils.decode(geohash, SpatialContext.GEO); if (!firstTime) { heatmapJs.append(","); } firstTime = false; heatmapJs.append("{lat: "); heatmapJs.append(hashCenter.getY()); heatmapJs.append(", lng: "); heatmapJs.append(hashCenter.getX()); heatmapJs.append(", count: "); heatmapJs.append(Long.toString(bucketCount)); heatmapJs.append("}"); if (bucketCount > max) { max = bucketCount; } if (bucketCount < min) { min = bucketCount; } } heatmapJs.append("],"); heatmapJs.append("max: "); heatmapJs.append(Long.toString(max)); heatmapJs.append(","); heatmapJs.append("min: "); heatmapJs.append(Long.toString(min - 1)); heatmapJs.append(","); heatmapJs.append("});"); heatmapJs.append("};"); } private static Client getEsClient() { // //////////// // Remote if (esClient == null) { try { final Settings settings = Settings.settingsBuilder() .put("node.name", "DRAGONFLY_" + Long.toString(Thread.currentThread().getId())) .put("cluster.name", "DRAGONFLY").build(); esClient = new TransportClient.Builder().settings(settings).build().addTransportAddress( new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300)); } catch (UnknownHostException ex) { LOGGER.log(Level.SEVERE, null, ex); } } return esClient; } /** * Checks if the specified field is a link based on the Elasticsearch index mapping metadata * * @param field * the field to check * @param meta * the index mapping metadata * @return true, if is link */ private static boolean isLink(final String fieldName, final Map<String, Object> meta) { String fieldInterface = ""; if (meta != null) { for (final Entry<String, Object> entry : meta.entrySet()) { if (entry.getKey().equals("Fields")) { // This is a field descriptor try { @SuppressWarnings("unchecked") final ArrayList<Map<String, Object>> descriptors = (ArrayList<Map<String, Object>>) entry .getValue(); for (final Map<String, Object> field : descriptors) { if (field.get("Name").equals(fieldName)) { fieldInterface = (String) field.get("Extended Type"); } } } catch (final Exception e) { // The meta data is a JSON object created by a query widget converted to a map of maps. // If the conventions were not followed, parsing will fail in many possible ways. // If parsing fails, this field will be treated as a normal field LOGGER.log(Level.SEVERE, e.getMessage(), e); } } } } return fieldInterface.equals("URL"); } private static String wrapAjaxJs(final String location, final String js) { final StringBuffer wrappedJs = new StringBuffer(); wrappedJs.append("try {"); wrappedJs.append(js); wrappedJs.append("} catch(err) { alert('ERROR at ").append(location).append(": ' + err); }"); return wrappedJs.toString(); } private final ModalWindow addWindow; private final ModalWindow informationWindow; private final String defaultTileServerConnection = "baseLayer = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n" + "attribution: '© <a href=\"http://openstreetmap.org\">OpenStreetMap</a> contributors, <a href=\"http://creativecommons.org/licenses/by-sa/2.0/\">CC-BY-SA</a>'\n" + "});\n"; private double east = 180; private final List<String> fieldList = new LinkedList<>(); private String filterTerm; private String geojsonField; private String geojsonIndex; private final MapSettingsPanel heatMapSettingsPanel; private final InformationPanel informationPanel; private String hits = "0"; private final Label hitsField; private String hmRadius; private String hmTransparency; // private final WebMarkupContainer mapDiv; private String mapIndex; private String mapLocationField; private double north = 90; private double south = -90; private final TextField<String> termFilterField; private String tileServerConnection; private transient ImmutableOpenMap<String, MappingMetaData> typeMappings; private double west = -180; private String wmsAttribution; private String wmsFormat; private String wmsLayers; private String wmsName; private String wmsTileUrl; private int zoomLevel = 3; private int zoomView; private String center = "[0,0]"; private final WebMarkupContainer controlBar; private void initializeFromSettingsFile() { final Properties properties = new Properties(); FileInputStream propertyStream = null; try { System.out.println("Current Path: " + Paths.get(".").toAbsolutePath().normalize().toString()); File propFile = new File("dragonfly/HeatMapWidget.properties"); System.out.println(propFile.getAbsolutePath()); propertyStream = new FileInputStream(propFile); properties.load(propertyStream); tileServerConnection = URLDecoder.decode(properties.getProperty("tileUrl", defaultTileServerConnection), "UTF-8"); } catch (final FileNotFoundException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } catch (final IOException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } finally { if (propertyStream != null) { try { propertyStream.close(); } catch (final IOException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } } } /** * Instantiates a new map widget page. * * @param parameters */ public MapWidgetPage(final PageParameters parameters) { initializeFromSettingsFile(); // ///////////////////////////// // Elasticsearch getEsClient(); // ///////////////////////////// // // Main panel controlBar = new WebMarkupContainer("controlBar"); controlBar.setOutputMarkupId(true); add(controlBar); final Form<?> queryForm = new Form<>("queryForm"); controlBar.add(queryForm); queryForm.add(new AddAjaxButton()); queryForm.add(new PermalinkAjaxButton()); final RefreshAjaxButton refreshButton = new RefreshAjaxButton(); queryForm.add(refreshButton); queryForm.setDefaultButton(refreshButton); termFilterField = new TextField<>("termFilter", new PropertyModel<String>(this, "filterTerm")); queryForm.add(termFilterField); hitsField = new Label("hits", new PropertyModel<>(this, "hits")); hitsField.setOutputMarkupId(true); queryForm.add(hitsField); // ///////////////////////////// // Map // mapDiv = new WebMarkupContainer("heatmapDiv"); // mapDiv.setOutputMarkupId(true); // add(mapDiv); add(new HandleOnZoomLeafletCallbackAjaxBehavior()); // Add Panel addWindow = new ModalWindow("dataPanel"); heatMapSettingsPanel = new MapSettingsPanel(addWindow.getContentId(), addWindow); addWindow.setContent(heatMapSettingsPanel); addWindow.setCookieName(null); addWindow.setInitialHeight(400); addWindow.setHeightUnit("px"); addWindow.setInitialWidth(572); addWindow.setWidthUnit("px"); addWindow.setResizable(false); addWindow.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() { private static final long serialVersionUID = 8118424010410661292L; @Override public void onClose(final AjaxRequestTarget target) { if (heatMapSettingsPanel.isOk()) { addDataToMap(heatMapSettingsPanel.getConfiguration(), target); } } }); add(addWindow); // Information Panel informationWindow = new ModalWindow("informationPanel"); informationPanel = new InformationPanel(informationWindow.getContentId()); informationWindow.setContent(informationPanel); informationWindow.setCookieName(null); informationWindow.setInitialHeight(200); informationWindow.setHeightUnit("px"); informationWindow.setInitialWidth(574); informationWindow.setWidthUnit("px"); informationWindow.setResizable(false); add(informationWindow); } @Override public void renderHead(final IHeaderResponse response) { super.renderHead(response); response.render(JavaScriptHeaderItem.forScript(tileServerConnection, null)); } private void addDataToHeatmap(final AjaxRequestTarget target, final String indexName, final String location) { final StringBuffer heatmapJs = new StringBuffer(); final StringBuffer markerJs = new StringBuffer(); hits = "0"; SearchResponse results; if ((indexName != null) && !indexName.isEmpty()) { try { final SearchRequestBuilder aggSearch = getEsClient().prepareSearch(indexName); aggSearch.addAggregation( geohashGrid("geohashgrid").field(location).precision(zoomLevel).size(MAX_GEOHASH_GRID)); aggSearch.setSearchType(SearchType.COUNT); QueryBuilder query = QueryBuilders.matchAllQuery(); if ((filterTerm != null) && !filterTerm.isEmpty()) { query = QueryBuilders.matchQuery("_all", filterTerm); } if ((south > -90) && (north < 90) && (east < 180) && (west > -180)) { final QueryBuilder geoQuery = QueryBuilders.geoBoundingBoxQuery(location).topLeft(north, west) .bottomRight(south, east); aggSearch.setQuery(QueryBuilders.boolQuery().must(query).must(geoQuery)); } else { aggSearch.setQuery(query); } results = aggSearch.execute().actionGet(); generateHeatmapJs(results, heatmapJs); hits = Long.toString(results.getHits().getTotalHits()); if (results.getHits().getTotalHits() < MAX_HITS_TO_SHOW) { final SearchRequestBuilder search = getEsClient().prepareSearch(indexName); search.setSearchType(SearchType.DFS_QUERY_THEN_FETCH); search.setSize(MAX_HITS_TO_SHOW); if ((south > -90) && (north < 90) && (east < 180) && (west > -180)) { final QueryBuilder geoQuery = QueryBuilders.geoBoundingBoxQuery(location) .topLeft(north, west).bottomRight(south, east); aggSearch.setQuery(QueryBuilders.boolQuery().must(query).must(geoQuery)); } else { aggSearch.setQuery(query); } generateMarkerJs(search.execute().actionGet(), markerJs, location); } } catch (final ElasticsearchException e) { LOGGER.log(Level.SEVERE, "Invalid query - return empty results.", e); } } target.add(hitsField); if (markerJs.length() > 0) { target.appendJavaScript(wrapAjaxJs("addDataToMap - markers", markerJs.toString())); } target.appendJavaScript(wrapAjaxJs("addDataToMap - heatmap", heatmapJs.toString())); } private void addDataToMap(final Map<String, String> configuration, final AjaxRequestTarget target) { final String dataType = configuration.get("Type"); if (dataType != null) { switch (dataType) { case "Query Results": mapIndex = configuration.get("Index"); mapLocationField = configuration.get("Location"); hmTransparency = configuration.get("HmTransparency"); hmRadius = configuration.get("HmRadius"); typeMappings = esGetMappings(mapIndex); addHeatmapToMap(target); addDataToHeatmap(target, mapIndex, mapLocationField); break; case "GeoJSON": geojsonIndex = configuration.get("Index"); geojsonField = configuration.get("GeojsonField"); addGeojsonToMap(target, geojsonIndex, geojsonField); break; case "WMS": wmsTileUrl = configuration.get("TileURL"); wmsLayers = configuration.get("Layers"); wmsFormat = configuration.get("Format"); wmsAttribution = configuration.get("Attibution"); wmsName = configuration.get("Name"); addWmsToMap(target, wmsTileUrl, wmsLayers, wmsFormat, wmsAttribution, wmsName); break; default: LOGGER.log(Level.SEVERE, "Unknown dataType {0}", dataType); break; } } } private void setMapViewableArea(final Map<String, String> configuration, final AjaxRequestTarget target) { final StringBuilder js = new StringBuilder(); js.append("\n\nmap.setView(L.latLng(").append(configuration.get("center")).append("), ") .append(configuration.get("zoomView")).append(");\n\n"); target.appendJavaScript(wrapAjaxJs("setMapViewableArea", js.toString())); } private void addHeatmapToMap(final AjaxRequestTarget target) { final StringBuffer js = new StringBuffer(); js.append("if(!(typeof heatmapLayer === 'undefined') && (heatmapLayer != null)) {"); js.append(" map.removeLayer(heatmapLayer);"); js.append(" controls.removeLayer(heatmapLayer);"); js.append(" markerLayer.clearLayers(); "); js.append("}"); js.append("heatmapLayer = new HeatmapOverlay({"); js.append(" radius: ").append(hmRadius).append(","); js.append(" opacity: ").append(hmTransparency).append(","); js.append(" scaleRadius: false,"); js.append(" useLocalExtrema: false,"); js.append(" latField: 'lat',"); js.append(" lngField: 'lng',"); js.append(" valueField: 'count'"); js.append(" });"); js.append("map.addLayer(heatmapLayer);"); js.append("controls.addOverlay(heatmapLayer,'Heat Map');"); target.appendJavaScript(wrapAjaxJs("addHeatmapToMap", js.toString())); } @SuppressWarnings("unchecked") private void generateMarkerJs(final SearchResponse results, final StringBuffer markerJs, final String locationField) { for (final SearchHit hit : results.getHits().getHits()) { final GeoPoint location = new GeoPoint(); location.resetFromString(hit.getSource().get(locationField).toString()); final BigDecimal lat = new BigDecimal(location.lat()).setScale(11, BigDecimal.ROUND_HALF_DOWN); final BigDecimal lon = new BigDecimal(location.lon()).setScale(11, BigDecimal.ROUND_HALF_DOWN); markerJs.append("markerLayer.addLayer(L.marker(["); markerJs.append(Double.toString(lat.doubleValue())); markerJs.append(","); markerJs.append(Double.toString(lon.doubleValue())); final StringBuffer markerText = new StringBuffer(); hit.getSource().entrySet().stream().forEach((fields) -> { String markerValue = (String) fields.getValue(); try { markerText.append("<b>"); markerText.append(fields.getKey()); markerText.append(" :</b> "); if (isLink(fields.getKey(), ((Map<String, Object>) typeMappings.get(hit.getType()) .getSourceAsMap().get("_meta")))) { markerText.append("<a href=\""); markerText.append(markerValue); markerText.append("\">"); markerText.append(markerValue); markerText.append("</a>"); } else { if (markerValue.length() > MAX_POPUP_ROW_LENGTH) { markerValue = markerValue.substring(0, MAX_POPUP_ROW_LENGTH) + " ..."; } markerText.append(StringEscapeUtils.escapeHtml(markerValue).replace('\n', ' ')); } markerText.append("<br>"); } catch (final IOException e) { LOGGER.log(Level.SEVERE, "Cannot access type mappings", e); } catch (final Exception e2) { LOGGER.log(Level.SEVERE, e2.getMessage(), e2); } }); markerJs.append("]).bindPopup('"); markerJs.append(markerText.toString().replace("'", " ")); markerJs.append("', {maxWidth: 500}));\n"); } } private void refreshDataOnMap(final Map<String, String> configuration, final AjaxRequestTarget target) { target.appendJavaScript("clearMap()"); if (mapIndex != null) { addDataToHeatmap(target, mapIndex, mapLocationField); } if (geojsonIndex != null) { addGeojsonToMap(target, geojsonIndex, geojsonField); } } /* * (non-Javadoc) * * @see org.apache.wicket.markup.html.WebPage#onAfterRender() */ @Override protected void onAfterRender() { super.onAfterRender(); // Increase the HTTP session timeout to 1 day ((HttpServletRequest) RequestCycle.get().getRequest().getContainerRequest()).getSession() .setMaxInactiveInterval(ONE_DAY_IN_SECONDS); } protected @Override void setHeaders(final org.apache.wicket.request.http.WebResponse response) { super.setHeaders(response); response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1. response.setHeader("Pragma", "no-cache"); // HTTP 1.0. response.setDateHeader("Expires", Time.START_OF_UNIX_TIME); // Proxies. } private String generatePermalink(final Map<String, String> configuration) { configuration.put("zoomLevel", Integer.toString(zoomLevel)); configuration.put("zoomView", Integer.toString(zoomView)); configuration.put("center", center); configuration.put("tileUrl", tileServerConnection); configuration.put("filter", filterTerm); configuration.put("north", Double.toString(north)); configuration.put("south", Double.toString(south)); configuration.put("east", Double.toString(east)); configuration.put("west", Double.toString(west)); StringBuilder permalink = new StringBuilder(); permalink.append(getRequestUrl()); permalink.append("?"); boolean firstTime = true; for (Entry<String, String> parameter : configuration.entrySet()) { try { if (parameter.getValue() != null) { if (!firstTime) { permalink.append("&"); } firstTime = false; permalink.append(URLEncoder.encode(parameter.getKey(), "UTF-8")); permalink.append("="); permalink.append(URLEncoder.encode(parameter.getValue(), "UTF-8")); } } catch (UnsupportedEncodingException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } return permalink.toString(); } private String getRequestUrl() { return RequestCycle.get().getUrlRenderer() .renderFullUrl(Url.parse(urlFor(MapWidgetBMPage.class, null).toString())); } }