Java tutorial
/* * Copyright 2016 Esri, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.esri.geoportal.commons.agp.client; import com.esri.geoportal.commons.utils.SimpleCredentials; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.client.HttpResponseException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; /** * ArcGIS Portal client. */ public class AgpClient implements Closeable { private static final String QUERY_EXTRAS = "-type:\"Layer\" -type: \"Map Document\" -type:\"Map Package\" -type:\"Basemap Package\" -type:\"Mobile Basemap Package\" -type:\"Mobile Map Package\" -type:\"ArcPad Package\" -type:\"Project Package\" -type:\"Project Template\" -type:\"Desktop Style\" -type:\"Pro Map\" -type:\"Layout\" -type:\"Explorer Map\" -type:\"Globe Document\" -type:\"Scene Document\" -type:\"Published Map\" -type:\"Map Template\" -type:\"Windows Mobile Package\" -type:\"Layer Package\" -type:\"Explorer Layer\" -type:\"Geoprocessing Package\" -type:\"Desktop Application Template\" -type:\"Code Sample\" -type:\"Geoprocessing Package\" -type:\"Geoprocessing Sample\" -type:\"Locator Package\" -type:\"Workflow Manager Package\" -type:\"Windows Mobile Package\" -type:\"Explorer Add In\" -type:\"Desktop Add In\" -type:\"File Geodatabase\" -type:\"Feature Collection Template\" -type:\"Code Attachment\" -type:\"Featured Items\" -type:\"Symbol Set\" -type:\"Color Set\" -type:\"Windows Viewer Add In\" -type:\"Windows Viewer Configuration\""; private final URL rootUrl; private final SimpleCredentials credentials; private final CloseableHttpClient httpClient; /** * Creates instance of the client. * @param rootUrl root URL * @param credentials credentials */ public AgpClient(URL rootUrl, SimpleCredentials credentials) { this.rootUrl = adjustUrl(rootUrl); this.credentials = credentials; this.httpClient = HttpClients.createDefault(); } @Override public void close() throws IOException { httpClient.close(); } /** * Adds item. * @param owner owner * @param folderId folder id (optional) * @param title title * @param description description * @param text text * @param thumbnailUrl thumbnail url * @param itemType item type (must be a URL type) * @param extent extent * @param typeKeywords type keywords * @param tags tags tags * @param token token * @return add item response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public ItemResponse addItem(String owner, String folderId, String title, String description, String text, URL thumbnailUrl, ItemType itemType, Double[] extent, String[] typeKeywords, String[] tags, String token) throws IOException, URISyntaxException { URIBuilder builder = new URIBuilder(addItemUri(owner, StringUtils.trimToNull(folderId))); HttpPost req = new HttpPost(builder.build()); HashMap<String, String> params = new HashMap<>(); params.put("f", "json"); params.put("title", title); params.put("description", description); params.put("type", itemType.getTypeName()); params.put("text", text); if (thumbnailUrl != null) { params.put("thumbnailurl", thumbnailUrl.toExternalForm()); } if (extent != null && extent.length == 4) { params.put("extent", Arrays.asList(extent).stream().map(Object::toString).collect(Collectors.joining(","))); } if (typeKeywords != null) { params.put("typeKeywords", Arrays.asList(typeKeywords).stream().collect(Collectors.joining(","))); } if (tags != null) { params.put("tags", Arrays.asList(tags).stream().collect(Collectors.joining(","))); } params.put("token", token); req.setEntity(createEntity(params)); return execute(req, ItemResponse.class); } /** * Adds item. * @param owner user name * @param folderId folder id (optional) * @param title title * @param description description * @param url URL * @param thumbnailUrl thumbnail url * @param itemType item type (must be a URL type) * @param extent extent * @param typeKeywords type keywords * @param tags tags tags * @param token token * @return add item response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public ItemResponse addItem(String owner, String folderId, String title, String description, URL url, URL thumbnailUrl, ItemType itemType, Double[] extent, String[] typeKeywords, String[] tags, String token) throws IOException, URISyntaxException { URIBuilder builder = new URIBuilder(addItemUri(owner, StringUtils.trimToNull(folderId))); HttpPost req = new HttpPost(builder.build()); HashMap<String, String> params = new HashMap<>(); params.put("f", "json"); params.put("title", title); params.put("description", description); params.put("type", itemType.getTypeName()); params.put("url", url.toExternalForm()); if (thumbnailUrl != null) { params.put("thumbnailurl", thumbnailUrl.toExternalForm()); } if (extent != null && extent.length == 4) { params.put("extent", Arrays.asList(extent).stream().map(Object::toString).collect(Collectors.joining(","))); } if (typeKeywords != null) { params.put("typeKeywords", Arrays.asList(typeKeywords).stream().collect(Collectors.joining(","))); } if (tags != null) { params.put("tags", Arrays.asList(tags).stream().collect(Collectors.joining(","))); } params.put("token", token); req.setEntity(createEntity(params)); return execute(req, ItemResponse.class); } /** * Updates item item. * @param owner user name * @param folderId folder id (optional) * @param itemId item id * @param title title * @param description description * @param text text * @param thumbnailUrl thumbnail URL * @param itemType item type (must be a URL type) * @param extent extent * @param typeKeywords type keywords * @param tags tags tags * @param token token * @return add item response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public ItemResponse updateItem(String owner, String folderId, String itemId, String title, String description, String text, URL thumbnailUrl, ItemType itemType, Double[] extent, String[] typeKeywords, String[] tags, String token) throws IOException, URISyntaxException { URIBuilder builder = new URIBuilder(updateItemUri(owner, StringUtils.trimToNull(folderId), itemId)); HttpPost req = new HttpPost(builder.build()); HashMap<String, String> params = new HashMap<>(); params.put("f", "json"); params.put("title", title); params.put("description", description); params.put("type", itemType.getTypeName()); params.put("text", text); if (thumbnailUrl != null) { params.put("thumbnailurl", thumbnailUrl.toExternalForm()); } if (extent != null && extent.length == 4) { params.put("extent", Arrays.asList(extent).stream().map(Object::toString).collect(Collectors.joining(","))); } if (typeKeywords != null) { params.put("typeKeywords", Arrays.asList(typeKeywords).stream().collect(Collectors.joining(","))); } if (tags != null) { params.put("tags", Arrays.asList(tags).stream().collect(Collectors.joining(","))); } params.put("token", token); req.setEntity(createEntity(params)); return execute(req, ItemResponse.class); } /** * Reads item information. * @param itemId item id * @param token token * @return item entry * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public ItemEntry readItem(String itemId, String token) throws IOException, URISyntaxException { URIBuilder builder = new URIBuilder(itemInfoUri(itemId)); builder.setParameter("f", "json"); if (token != null) { builder.setParameter("token", token); } HttpGet req = new HttpGet(builder.build()); return execute(req, ItemEntry.class); } /** * Adds item. * @param owner user name * @param folderId folder id (optional) * @param itemId item id * @param title title * @param description description * @param url URL * @param thumbnailUrl thumbnail URL * @param itemType item type (must be a URL type) * @param extent extent * @param typeKeywords type keywords * @param tags tags tags * @param token token * @return add item response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public ItemResponse updateItem(String owner, String folderId, String itemId, String title, String description, URL url, URL thumbnailUrl, ItemType itemType, Double[] extent, String[] typeKeywords, String[] tags, String token) throws IOException, URISyntaxException { URIBuilder builder = new URIBuilder(updateItemUri(owner, StringUtils.trimToNull(folderId), itemId)); HttpPost req = new HttpPost(builder.build()); HashMap<String, String> params = new HashMap<>(); params.put("f", "json"); params.put("title", title); params.put("description", description); params.put("type", itemType.getTypeName()); params.put("url", url.toExternalForm()); if (thumbnailUrl != null) { params.put("thumbnailurl", thumbnailUrl.toExternalForm()); } if (extent != null && extent.length == 4) { params.put("extent", Arrays.asList(extent).stream().map(Object::toString).collect(Collectors.joining(","))); } if (typeKeywords != null) { params.put("typeKeywords", Arrays.asList(typeKeywords).stream().collect(Collectors.joining(","))); } if (tags != null) { params.put("tags", Arrays.asList(tags).stream().collect(Collectors.joining(","))); } params.put("token", token); req.setEntity(createEntity(params)); return execute(req, ItemResponse.class); } /** * Sharing item. * @param owner user name * @param folderId folder id (optional) * @param itemId item id * @param everyone <code>true</code> to share with everyone * @param org <code>true</code> to share with group * @param groups list of groups to share with * @param token token * @return share response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public ShareResponse share(String owner, String folderId, String itemId, boolean everyone, boolean org, String[] groups, String token) throws URISyntaxException, IOException { URIBuilder builder = new URIBuilder(shareUri(owner, folderId, itemId)); HttpPost req = new HttpPost(builder.build()); HashMap<String, String> params = new HashMap<>(); params.put("f", "json"); params.put("everyone", Boolean.toString(everyone)); params.put("org", Boolean.toString(org)); params.put("groups", groups != null ? Arrays.asList(groups).stream().collect(Collectors.joining(",")) : ""); params.put("token", token); req.setEntity(createEntity(params)); return execute(req, ShareResponse.class); } /** * Deletes item. * @param owner owner * @param folderId folder id * @param itemId item id * @param token token * @return delete response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public DeleteResponse delete(String owner, String folderId, String itemId, String token) throws URISyntaxException, IOException { URIBuilder builder = new URIBuilder(deleteUri(owner, folderId, itemId)); builder.setParameter("f", "json"); builder.setParameter("token", token); HttpPost req = new HttpPost(builder.build()); return execute(req, DeleteResponse.class); } /** * Lists content. * @param owner owner * @param folderId folder id (optional) * @param num number items to return * @param start start item * @param token token (optional) * @return content response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public ContentResponse listContent(String owner, String folderId, long num, long start, String token) throws URISyntaxException, IOException { URIBuilder builder = new URIBuilder(userUri(owner, folderId)); builder.setParameter("f", "json"); builder.setParameter("num", Long.toString(num)); builder.setParameter("start", Long.toString(start)); if (token != null) { builder.setParameter("token", token); } HttpGet req = new HttpGet(builder.build()); return execute(req, ContentResponse.class); } /** * Searches for items. * @param query query * @param num max number of items * @param start start item * @param token token (optional) * @return query response * @throws URISyntaxException if invalid URL * @throws IOException if operation fails */ public QueryResponse search(String query, long num, long start, String token) throws URISyntaxException, IOException { URIBuilder builder = new URIBuilder(searchUri()); builder.setParameter("f", "json"); builder.setParameter("q", String.format("%s %s", query, QUERY_EXTRAS)); if (token != null) { builder.setParameter("token", token); } HttpGet req = new HttpGet(builder.build()); return execute(req, QueryResponse.class); } /** * Generates token. * * @param minutes expiration in minutes. * @return token response * @throws URISyntaxException if invalid URL * @throws IOException if accessing token fails */ public TokenResponse generateToken(int minutes) throws URISyntaxException, IOException { HttpPost req = new HttpPost(generateTokenUri()); HashMap<String, String> params = new HashMap<>(); params.put("f", "json"); if (credentials != null) { params.put("username", StringUtils.trimToEmpty(credentials.getUserName())); params.put("password", StringUtils.trimToEmpty(credentials.getPassword())); } params.put("client", "referer"); params.put("referer", InetAddress.getLocalHost().getHostAddress()); params.put("expiration", Integer.toString(minutes)); req.setEntity(createEntity(params)); return execute(req, TokenResponse.class); } private URI itemInfoUri(String itemId) throws URISyntaxException { URIBuilder builder = new URIBuilder(); builder.setScheme(rootUrl.toURI().getScheme()).setHost(rootUrl.toURI().getHost()) .setPort(rootUrl.toURI().getPort()) .setPath(rootUrl.toURI().getPath() + "sharing/content/items/" + itemId); return builder.build(); } private URI updateItemUri(String owner, String folderId, String itemId) throws URISyntaxException { URIBuilder builder = new URIBuilder(); builder.setScheme(rootUrl.toURI().getScheme()).setHost(rootUrl.toURI().getHost()) .setPort(rootUrl.toURI().getPort()) .setPath(rootUrl.toURI().getPath() + "sharing/rest/content/users/" + owner + (folderId != null ? "/" + folderId : "") + "/items/" + itemId + "/update"); return builder.build(); } private URI addItemUri(String owner, String folderId) throws URISyntaxException { URIBuilder builder = new URIBuilder(); builder.setScheme(rootUrl.toURI().getScheme()).setHost(rootUrl.toURI().getHost()) .setPort(rootUrl.toURI().getPort()) .setPath(rootUrl.toURI().getPath() + "sharing/rest/content/users/" + owner + (folderId != null ? "/" + folderId : "") + "/addItem"); return builder.build(); } private URI shareUri(String owner, String folderId, String itemId) throws URISyntaxException { URIBuilder builder = new URIBuilder(); builder.setScheme(rootUrl.toURI().getScheme()).setHost(rootUrl.toURI().getHost()) .setPort(rootUrl.toURI().getPort()) .setPath(rootUrl.toURI().getPath() + "sharing/rest/content/users/" + owner + (folderId != null ? "/" + folderId : "") + "/items/" + itemId + "/share"); return builder.build(); } private URI deleteUri(String owner, String folderId, String itemId) throws URISyntaxException { URIBuilder builder = new URIBuilder(); builder.setScheme(rootUrl.toURI().getScheme()).setHost(rootUrl.toURI().getHost()) .setPort(rootUrl.toURI().getPort()) .setPath(rootUrl.toURI().getPath() + "sharing/rest/content/users/" + owner + (folderId != null ? "/" + folderId : "") + "/items/" + itemId + "/delete"); return builder.build(); } private URI userUri(String owner, String folderId) throws URISyntaxException { URIBuilder builder = new URIBuilder(); builder.setScheme(rootUrl.toURI().getScheme()).setHost(rootUrl.toURI().getHost()) .setPort(rootUrl.toURI().getPort()).setPath(rootUrl.toURI().getPath() + "sharing/rest/content/users/" + owner + (folderId != null ? "/" + folderId : "")); return builder.build(); } private URI searchUri() throws URISyntaxException { URIBuilder builder = new URIBuilder(); builder.setScheme(rootUrl.toURI().getScheme()).setHost(rootUrl.toURI().getHost()) .setPort(rootUrl.toURI().getPort()).setPath(rootUrl.toURI().getPath() + "sharing/rest/search"); return builder.build(); } private URI generateTokenUri() throws URISyntaxException { URI uri = rootUrl.toURI().resolve("sharing/rest/generateToken"); return new URI("https", uri.getSchemeSpecificPart(), uri.getFragment()); } private HttpEntity createEntity(Map<String, String> params) throws UnsupportedEncodingException { return new UrlEncodedFormEntity(params.entrySet().stream() .map(e -> new BasicNameValuePair(e.getKey(), e.getValue())).collect(Collectors.toList())); } private <T> T execute(HttpUriRequest req, Class<T> clazz) throws IOException { try (CloseableHttpResponse httpResponse = httpClient.execute(req); InputStream contentStream = httpResponse.getEntity().getContent();) { if (httpResponse.getStatusLine().getStatusCode() >= 400) { throw new HttpResponseException(httpResponse.getStatusLine().getStatusCode(), httpResponse.getStatusLine().getReasonPhrase()); } String responseContent = IOUtils.toString(contentStream, "UTF-8"); ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); return mapper.readValue(responseContent, clazz); } } private URL adjustUrl(URL rootUrl) { try { return new URL(rootUrl.toExternalForm().replaceAll("/*$", "/")); } catch (MalformedURLException ex) { return rootUrl; } } }