Java tutorial
/* * Copyright (C) 2012-2014 Jaspersoft Corporation. All rights reserved. * http://community.jaspersoft.com/project/mobile-sdk-android * * Unless you have purchased a commercial license agreement from Jaspersoft, * the following license terms apply: * * This program is part of Jaspersoft Mobile SDK for Android. * * Jaspersoft Mobile SDK 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 * (at your option) any later version. * * Jaspersoft Mobile SDK 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. * * You should have received a copy of the GNU Lesser General Public License * along with Jaspersoft Mobile SDK for Android. If not, see * <http://www.gnu.org/licenses/lgpl>. */ package com.jaspersoft.android.sdk.client; import com.jaspersoft.android.sdk.client.oxm.ReportDescriptor; import com.jaspersoft.android.sdk.client.oxm.ResourceDescriptor; import com.jaspersoft.android.sdk.client.oxm.ResourceParameter; import com.jaspersoft.android.sdk.client.oxm.ResourceProperty; import com.jaspersoft.android.sdk.client.oxm.ResourcesList; import com.jaspersoft.android.sdk.client.oxm.control.InputControl; import com.jaspersoft.android.sdk.client.oxm.control.InputControlState; import com.jaspersoft.android.sdk.client.oxm.control.InputControlStatesList; import com.jaspersoft.android.sdk.client.oxm.control.InputControlsList; import com.jaspersoft.android.sdk.client.oxm.report.ExportExecution; import com.jaspersoft.android.sdk.client.oxm.report.ExportsRequest; import com.jaspersoft.android.sdk.client.oxm.report.FolderDataResponse; import com.jaspersoft.android.sdk.client.oxm.report.ReportDataResponse; import com.jaspersoft.android.sdk.client.oxm.report.ReportExecutionRequest; import com.jaspersoft.android.sdk.client.oxm.report.ReportExecutionResponse; import com.jaspersoft.android.sdk.client.oxm.report.ReportParameter; import com.jaspersoft.android.sdk.client.oxm.report.ReportParametersList; import com.jaspersoft.android.sdk.client.oxm.report.ReportStatusResponse; import com.jaspersoft.android.sdk.client.oxm.resource.ResourceLookupSearchCriteria; import com.jaspersoft.android.sdk.client.oxm.resource.ResourceLookupsList; import com.jaspersoft.android.sdk.client.oxm.server.ServerInfo; import com.jaspersoft.android.sdk.util.CookieHttpRequestInterceptor; import com.jaspersoft.android.sdk.util.KeepAliveHttpRequestInterceptor; import com.jaspersoft.android.sdk.util.LocalesHttpRequestInterceptor; import com.jaspersoft.android.sdk.util.StaticCacheHelper; import org.simpleframework.xml.Serializer; import org.simpleframework.xml.convert.AnnotationStrategy; import org.simpleframework.xml.core.Persister; import org.simpleframework.xml.strategy.Strategy; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.xml.SimpleXmlHttpMessageConverter; import org.springframework.util.FileCopyUtils; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriTemplate; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import static java.util.Collections.singletonList; /** * The central class that provides a set of convenient methods to interact with the JasperReports Server REST API * and does mapping of the returned data to object model. * * @author Ivan Gadzhega * @since 1.0 */ public class JsRestClient { public static final String REST_SERVICES_URI = "/rest"; public static final String REST_SERVICES_V2_URI = "/rest_v2"; public static final String REST_RESOURCE_URI = "/resource"; public static final String REST_RESOURCES_URI = "/resources"; public static final String REST_REPORT_URI = "/report"; public static final String REST_REPORTS_URI = "/reports"; public static final String REST_INPUT_CONTROLS_URI = "/inputControls"; public static final String REST_VALUES_URI = "/values"; public static final String REST_SERVER_INFO_URI = "/serverInfo"; public static final String REST_REPORT_EXECUTIONS = "/reportExecutions"; public static final String REST_REPORT_EXPORTS = "/exports"; public static final String REST_REPORT_STATUS = "/status"; public static final String REST_THUMBNAILS = "/thumbnails"; private SimpleXmlHttpMessageConverter simpleXmlHttpMessageConverter; // the timeout in milliseconds until a connection is established private int connectTimeout = 15 * 1000; // the socket timeout in milliseconds for waiting for data private int readTimeout = 120 * 1000; private JsServerProfile jsServerProfile; private String restServicesUrl; private ServerInfo serverInfo; private final RestTemplate restTemplate; private final SimpleClientHttpRequestFactory requestFactory; //--------------------------------------------------------------------- // Constructors //--------------------------------------------------------------------- public JsRestClient() { this(new RestTemplate(true), new SimpleClientHttpRequestFactory()); } public JsRestClient(RestTemplate restTemplate) { this(restTemplate, new SimpleClientHttpRequestFactory()); } public JsRestClient(RestTemplate restTemplate, SimpleClientHttpRequestFactory factory) { this.restTemplate = restTemplate; this.requestFactory = factory; fetchXmlConverter(); configureAnnotationStrategy(); } //--------------------------------------------------------------------- // Timeouts //--------------------------------------------------------------------- /** * Set the underlying URLConnection's connect timeout. A timeout value of 0 specifies an infinite timeout. * * @param timeout the timeout value in milliseconds * @since 1.5 */ public void setConnectTimeout(int timeout) { connectTimeout = timeout; updateConnectTimeout(); } /** * Set the underlying URLConnection's read timeout. A timeout value of 0 specifies an infinite timeout. * * @param timeout the timeout value in milliseconds * @since 1.5 */ public void setReadTimeout(int timeout) { readTimeout = timeout; updateReadTimeout(); } //--------------------------------------------------------------------- // Server Profiles & Info //--------------------------------------------------------------------- public JsServerProfile getServerProfile() { return jsServerProfile; } public void setServerProfile(final JsServerProfile serverProfile) { this.serverInfo = null; this.jsServerProfile = serverProfile; // We allow user to set profile to null value if (jsServerProfile != null) { this.restServicesUrl = serverProfile.getServerUrl() + REST_SERVICES_URI; updateRequestFactoryTimeouts(); restTemplate.setRequestFactory(requestFactory); List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>(); interceptors.add(new LocalesHttpRequestInterceptor()); interceptors.add(new CookieHttpRequestInterceptor(jsServerProfile)); interceptors.add(new KeepAliveHttpRequestInterceptor()); restTemplate.setInterceptors(interceptors); } } /** * Gets server information details * * @return the ServerInfo value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.4 */ public ServerInfo getServerInfo() throws RestClientException { return getServerInfo(false); } /** * Gets server information details * * @param forceUpdate set to <code>true</code> to force update of the server info * @return the ServerInfo value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.4 */ public ServerInfo getServerInfo(boolean forceUpdate) throws RestClientException { if (forceUpdate || serverInfo == null) { String uri = getServerProfile().getServerUrl() + REST_SERVICES_V2_URI + REST_SERVER_INFO_URI; try { serverInfo = restTemplate.getForObject(uri, ServerInfo.class); } catch (HttpStatusCodeException ex) { HttpStatus statusCode = ex.getStatusCode(); if (statusCode == HttpStatus.NOT_FOUND) { serverInfo = new ServerInfo(); } else { throw ex; } } } return serverInfo; } //--------------------------------------------------------------------- // The Resource Service //--------------------------------------------------------------------- /** * Gets the resource descriptor for the resource with specified URI. * * @param uri resource URI (e.g. /reports/samples/) * @return the ResourceDescriptor value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourceDescriptor getResource(String uri) throws RestClientException { String fullUri = restServicesUrl + REST_RESOURCE_URI + uri; return restTemplate.getForObject(fullUri, ResourceDescriptor.class); } /** * Modifies the resource with specified ResourceDescriptor * * @param resourceDescriptor ResourceDescriptor of resource being modified * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public void modifyResource(ResourceDescriptor resourceDescriptor) throws RestClientException { String fullUri = restServicesUrl + REST_RESOURCE_URI + resourceDescriptor.getUriString(); restTemplate.postForLocation(fullUri, resourceDescriptor); } /** * Deletes the resource with the specified URI * * @param uri resource URI (e.g. /reports/samples/) * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public void deleteResource(String uri) throws RestClientException { String fullUri = restServicesUrl + REST_RESOURCE_URI + uri; restTemplate.delete(fullUri); } //--------------------------------------------------------------------- // The Resources Service //--------------------------------------------------------------------- /** * Gets the list of resource descriptors for all resources available in the folder specified in the URI. * * @param uri folder URI (e.g. /reports/samples/) * @return the list of ResourceDescriptor values * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public List<ResourceDescriptor> getResourcesList(String uri) throws RestClientException { return getResources(uri).getResourceDescriptors(); } /** * Gets the list of resource descriptors for all resources available in the folder specified in the URI. * * @param uri folder URI (e.g. /reports/samples/) * @return the ResourcesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourcesList getResources(String uri) throws RestClientException { String fullUri = restServicesUrl + REST_RESOURCES_URI + uri; return restTemplate.getForObject(fullUri, ResourcesList.class); } /** * Gets the list of resource descriptors for the resources available in the folder specified in the URI * and matching the specified parameters. * * @param uri repository URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description (can be <code>null</code>) * @param recursive Get resources recursively and not only in the specified URI. Used only when a search criteria * is specified, either query or type. (can be <code>null</code>) * @param limit Maximum number of items returned to the client. The default is 0 (can be <code>null</code>), * meaning no limit. * @return the list of ResourceDescriptor values * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public List<ResourceDescriptor> getResourcesList(String uri, String query, Boolean recursive, Integer limit) throws RestClientException { return getResources(uri, query, recursive, limit).getResourceDescriptors(); } /** * Gets the list of resource descriptors for the resources available in the folder specified in the URI * and matching the specified parameters. * * @param uri repository URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description (can be <code>null</code>) * @param recursive Get resources recursively and not only in the specified URI. Used only when a search criteria * is specified, either query or type. (can be <code>null</code>) * @param limit Maximum number of items returned to the client. The default is 0 (can be <code>null</code>), * meaning no limit. * @return the ResourcesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourcesList getResources(String uri, String query, Boolean recursive, Integer limit) throws RestClientException { String uriVariablesTemplate = "?q={query}&recursive={recursive}&limit={limit}"; String fullUri = restServicesUrl + REST_RESOURCES_URI + uri + uriVariablesTemplate; return restTemplate.getForObject(fullUri, ResourcesList.class, query, recursive, limit); } /** * Gets the list of resource descriptors for the resources available in the folder specified in the URI * and matching the specified parameters. * * @param uri repository URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description (can be <code>null</code>) * @param type Match only resources of the given type * @param recursive Get resources recursively and not only in the specified URI. Used only when a search criteria * is specified, either query or type. (can be <code>null</code>) * @param limit Maximum number of items returned to the client. The default is 0 (can be <code>null</code>), * meaning no limit. * @return the list of ResourceDescriptor values * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public List<ResourceDescriptor> getResourcesList(String uri, String query, String type, Boolean recursive, Integer limit) throws RestClientException { return getResources(uri, query, type, recursive, limit).getResourceDescriptors(); } /** * Gets the list of resource descriptors for the resources available in the folder specified in the URI * and matching the specified parameters. * * @param uri repository URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description (can be <code>null</code>) * @param type Match only resources of the given type * @param recursive Get resources recursively and not only in the specified URI. Used only when a search criteria * is specified, either query or type. (can be <code>null</code>) * @param limit Maximum number of items returned to the client. The default is 0 (can be <code>null</code>), * meaning no limit. * @return the ResourcesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourcesList getResources(String uri, String query, String type, Boolean recursive, Integer limit) throws RestClientException { String uriVariablesTemplate = "?q={query}&type={type}&recursive={recursive}&limit={limit}"; String fullUri = restServicesUrl + REST_RESOURCES_URI + uri + uriVariablesTemplate; return restTemplate.getForObject(fullUri, ResourcesList.class, query, type, recursive, limit); } /** * Gets the list of resource descriptors for the resources available in the folder specified in the URI * and matching the specified parameters. * * @param uri repository URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description (can be <code>null</code>) * @param types Match only resources of the given types * @param recursive Get resources recursively and not only in the specified URI. Used only when a search criteria * is specified, either query or type. (can be <code>null</code>) * @param limit Maximum number of items returned to the client. The default is 0 (can be <code>null</code>), * meaning no limit. * @return the list of ResourceDescriptor values * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public List<ResourceDescriptor> getResourcesList(String uri, String query, List<String> types, Boolean recursive, Integer limit) throws RestClientException { return getResources(uri, query, types, recursive, limit).getResourceDescriptors(); } /** * Gets the list of resource descriptors for the resources available in the folder specified in the URI * and matching the specified parameters. * * @param uri repository URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description (can be <code>null</code>) * @param types Match only resources of the given types * @param recursive Get resources recursively and not only in the specified URI. Used only when a search criteria * is specified, either query or type. (can be <code>null</code>) * @param limit Maximum number of items returned to the client. The default is 0 (can be <code>null</code>), * meaning no limit. * @return the ResourcesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourcesList getResources(String uri, String query, List<String> types, Boolean recursive, Integer limit) throws RestClientException { StringBuilder fullUri = new StringBuilder(); fullUri.append(restServicesUrl).append(REST_RESOURCES_URI).append(uri); fullUri.append("?q={query}&recursive={recursive}&limit={limit}"); if (types != null) { for (String type : types) { fullUri.append("&type=").append(type); } } return restTemplate.getForObject(fullUri.toString(), ResourcesList.class, query, recursive, limit); } //--------------------------------------------------------------------- // The Resources Service v2 //--------------------------------------------------------------------- /** * Retrieves the list of resource lookup objects for the resources contained in the given parent folder * and matching the specified parameters. * * @param folderUri parent folder URI (e.g. /reports/samples/) * @param recursive Get resources recursively * @param offset Pagination. Start index for requested page. * @param limit Pagination. Resources count per page. * @return the ResourceLookupsList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourceLookupsList getResourceLookups(String folderUri, boolean recursive, int offset, int limit) throws RestClientException { return getResourceLookups(folderUri, null, null, recursive, offset, limit); } /** * Retrieves the list of resource lookup objects for the resources contained in the given parent folder * and matching the specified parameters. * * @param folderUri parent folder URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description. * (can be <code>null</code>) * @param types Match only resources of the given types. Multiple resource types allowed. * (can be <code>null</code>) * @param recursive Get resources recursively * @param offset Pagination. Start index for requested page. * @param limit Pagination. Resources count per page. * @return the ResourceLookupsList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourceLookupsList getResourceLookups(String folderUri, String query, List<String> types, boolean recursive, int offset, int limit) throws RestClientException { return getResourceLookups(folderUri, query, types, null, recursive, offset, limit); } /** * Retrieves the list of resource lookup objects for the resources contained in the given parent folder * and matching the specified parameters. * * @param folderUri parent folder URI (e.g. /reports/samples/) * @param query Match only resources having the specified text in the name or description. * (can be <code>null</code>) * @param types Match only resources of the given types. Multiple resource types allowed. * (can be <code>null</code>) * @param sortBy Represents a field in the results to sort by: uri, label, description, type, creationDate, * updateDate, accessTime, or popularity (based on access events). * (can be <code>null</code>) * @param recursive Get resources recursively * @param offset Pagination. Start index for requested page. * @param limit Pagination. Resources count per page. * @return the ResourceLookupsList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourceLookupsList getResourceLookups(String folderUri, String query, List<String> types, String sortBy, boolean recursive, int offset, int limit) throws RestClientException { ResourceLookupSearchCriteria criteria = new ResourceLookupSearchCriteria(); criteria.setFolderUri(folderUri); criteria.setQuery(query); criteria.setTypes(types); criteria.setSortBy(sortBy); criteria.setRecursive(recursive); criteria.setOffset(offset); criteria.setLimit(limit); return getResourceLookups(criteria); } /** * Retrieves the list of resource lookup objects matching the specified search criteria. * * @param searchCriteria the search criteria * @return the ResourceLookupsList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourceLookupsList getResourceLookups(ResourceLookupSearchCriteria searchCriteria) throws RestClientException { StringBuilder fullUri = new StringBuilder(); fullUri.append(getServerProfile().getServerUrl()).append(REST_SERVICES_V2_URI).append(REST_RESOURCES_URI) .append("?folderUri={folderUri}").append("&q={query}").append("&sortBy={sortBy}") .append("&recursive={recursive}").append("&forceFullPage={forceFullPage}") .append("&offset={offset}").append("&limit={limit}"); if (searchCriteria.getTypes() != null) { for (String type : searchCriteria.getTypes()) { fullUri.append("&type=").append(type); } } ResponseEntity<ResourceLookupsList> responseEntity = restTemplate.exchange(fullUri.toString(), HttpMethod.GET, null, ResourceLookupsList.class, searchCriteria.getFolderUri(), searchCriteria.getQuery(), searchCriteria.getSortBy(), searchCriteria.isRecursive(), searchCriteria.isForceFullPage(), searchCriteria.getOffset(), searchCriteria.getLimit()); if (responseEntity.getStatusCode() == HttpStatus.NO_CONTENT) { return new ResourceLookupsList(); } else { ResourceLookupsList resourceLookupsList = responseEntity.getBody(); resourceLookupsList.setResultCount(responseEntity.getHeaders().getFirst("Result-Count")); resourceLookupsList.setTotalCount(responseEntity.getHeaders().getFirst("Total-Count")); resourceLookupsList.setStartIndex(responseEntity.getHeaders().getFirst("Start-Index")); resourceLookupsList.setNextOffset(responseEntity.getHeaders().getFirst("Next-Offset")); return resourceLookupsList; } } /** * Retrives all data for the root folder */ public FolderDataResponse getRootFolderData() { String fullUri = jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_RESOURCES_URI; HttpHeaders headers = new HttpHeaders(); headers.add("Accept", "application/repository.folder+xml"); headers.add("Content-Type", "application/xml"); HttpEntity<String> httpEntity = new HttpEntity<String>("", headers); ResponseEntity<FolderDataResponse> responseEntity = restTemplate.exchange(fullUri, HttpMethod.GET, httpEntity, FolderDataResponse.class); return responseEntity.getBody(); } //--------------------------------------------------------------------- // The Report Service //--------------------------------------------------------------------- /** * Runs the report and generates the specified output. The response contains report descriptor * with the ID of the saved output for downloading later with a GET request. * * @param resourceDescriptor resource descriptor of this report * @param format The format of the report output. Possible values: PDF, HTML, XLS, RTF, CSV, * XML, JRPRINT. The Default is PDF. * @return ReportDescriptor * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ReportDescriptor getReportDescriptor(ResourceDescriptor resourceDescriptor, String format) throws RestClientException { String fullUri = restServicesUrl + REST_REPORT_URI + resourceDescriptor.getUriString() + "?IMAGES_URI=./&RUN_OUTPUT_FORMAT={format}"; HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.setAccept(singletonList(MediaType.TEXT_XML)); HttpEntity<ResourceDescriptor> requestEntity = new HttpEntity<ResourceDescriptor>(resourceDescriptor, requestHeaders); ResponseEntity<ReportDescriptor> entity = restTemplate.exchange(fullUri, HttpMethod.PUT, requestEntity, ReportDescriptor.class, format); return entity.getBody(); } /** * Downloads specified report attachment, once a report has been generated, and keeps it in memory as a byte array. * * @param uuid Universally Unique Identifier. As a side effect of storing the report output in the user session, * the UUID in the URI is visible only to the currently logged in user. * @param name One of the file names specified in the report xml. If the file parameter is not specified, * the service returns the report descriptor. * @return Attachment file as byte array stored in memory. * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public byte[] getReportAttachment(String uuid, String name) throws RestClientException { String fullUri = restServicesUrl + REST_REPORT_URI + "/{uuid}?file={name}"; HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.setAccept(singletonList(MediaType.APPLICATION_OCTET_STREAM)); ResponseEntity<byte[]> entity = restTemplate.exchange(fullUri, HttpMethod.GET, new HttpEntity<byte[]>(requestHeaders), byte[].class, uuid, name); return entity.getBody(); } /** * Downloads specified report attachment, once a report has been generated and saves it in the specified file. * * @param uuid Universally Unique Identifier. As a side effect of storing the report output in the user session, * the UUID in the URI is visible only to the currently logged in user. * @param name One of the file names specified in the report xml. If the file parameter is not specified, * the service returns the report descriptor. * @param file The file in which the attachment will be saved. * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public void saveReportAttachmentToFile(String uuid, String name, File file) throws RestClientException { String fullUri = restServicesUrl + REST_REPORT_URI + "/{uuid}?file={name}"; UriTemplate uriTemplate = new UriTemplate(fullUri); URI expandedUri = uriTemplate.expand(uuid, name); downloadFile(expandedUri, file); } //--------------------------------------------------------------------- // The Report Service v2 //--------------------------------------------------------------------- /** * Generates the fully qualified report URL to receive all pages report output in HTML format. * * @param reportUri repository URI of the report * @param parameters list of report parameter/input control values * @return the fully qualified report URL * @since 1.4 */ public String generateReportUrl(String reportUri, List<ReportParameter> parameters) { return generateReportUrl(reportUri, parameters, 0, "HTML"); } /** * Generates the fully qualified report URL to receive all pages report output in specified format. * * @param reportUri repository URI of the report * @param parameters list of report parameter/input control values * @param format the format of the report output. Possible values: PDF, HTML, XLS, RTF, CSV, XML. * @return the fully qualified report URL * @since 1.4 */ public String generateReportUrl(String reportUri, List<ReportParameter> parameters, String format) { return generateReportUrl(reportUri, parameters, 0, format); } /** * Generates the fully qualified report URL according to specified parameters. The new v2/reports service allows clients * to receive report output in a single request-response using this url. * * @param reportUri repository URI of the report * @param parameters list of report parameter/input control values * @param page a positive integer value used to output a specific page or 0 to output all pages * @param format the format of the report output. Possible values: PDF, HTML, XLS, RTF, CSV, XML. * @return the fully qualified report URL * @since 1.4 */ public String generateReportUrl(String reportUri, List<ReportParameter> parameters, int page, String format) { StringBuilder reportUrl = new StringBuilder(); reportUrl.append(getServerProfile().getServerUrl()).append(REST_SERVICES_V2_URI); reportUrl.append(REST_REPORTS_URI).append(reportUri).append(".").append(format); if (parameters == null) parameters = new ArrayList<ReportParameter>(); if (page > 0) parameters.add(new ReportParameter("page", Integer.toString(page))); if (!parameters.isEmpty()) { reportUrl.append("?"); Iterator<ReportParameter> paramIterator = parameters.iterator(); while (paramIterator.hasNext()) { ReportParameter parameter = paramIterator.next(); Iterator<String> valueIterator = parameter.getValues().iterator(); while (valueIterator.hasNext()) { try { String value = URLEncoder.encode(valueIterator.next(), "UTF-8"); reportUrl.append(parameter.getName()).append("=").append(value); if (paramIterator.hasNext() || valueIterator.hasNext()) reportUrl.append("&"); } catch (UnsupportedEncodingException ex) { throw new IllegalArgumentException(ex); } } } } return reportUrl.toString(); } /** * Downloads report output, once it has been generated and saves it in the specified file. * * @param reportUrl the fully qualified report URL * @param file The file in which the output will be saved * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.4 */ public void saveReportOutputToFile(String reportUrl, File file) throws RestClientException { URI uri; try { uri = new URI(reportUrl); } catch (URISyntaxException ex) { throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex); } downloadFile(uri, file); } //--------------------------------------------------------------------- // Report Execution Service //--------------------------------------------------------------------- /** * Forms and executes url "{server url}/rest_v2/reportExecutions" * * @param request we delegate to the restTemplate. * @return report execution response. Includes executionId for later use. * @throws RestClientException */ public ReportExecutionResponse runReportExecution(ReportExecutionRequest request) throws RestClientException { String url = jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_REPORT_EXECUTIONS; return restTemplate.postForObject(url, request, ReportExecutionResponse.class); } /** * Sends request with porpose to fetch current export datum. * * @param executionId Identifies current id of running report. * @param request we delegate to the restTemplate. * @return response with all exports datum associated with request. * @throws RestClientException */ public ExportExecution runExportForReport(String executionId, ExportsRequest request) throws RestClientException { return restTemplate.postForObject(getExportForReportURI(executionId), request, ExportExecution.class); } /** * Generates link for requesting data on specified export resource. * * @param executionId Identifies current id of running report. * @return "{server url}/rest_v2/reportExecutions/{executionId}/exports" */ public URI getExportForReportURI(String executionId) { String outputResourceUri = "/{executionId}"; String fullUri = jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_REPORT_EXECUTIONS + outputResourceUri + REST_REPORT_EXPORTS; return new UriTemplate(fullUri).expand(executionId, executionId); } /** * Sends request for the current running report for the status check. * * @param executionId Identifies current id of running report. * @return response which expose current report status. */ public ReportStatusResponse runReportStatusCheck(String executionId) { return restTemplate.getForObject(getReportStatusCheckURI(executionId), ReportStatusResponse.class); } /** * Generates link for requesting report execution status. * * @param executionId Identifies current id of running report. * @return "{server url}/rest_v2/reportExecutions/{executionId}/status" */ public URI getReportStatusCheckURI(String executionId) { String outputResourceUri = "/{executionId}"; String fullUri = jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_REPORT_EXECUTIONS + outputResourceUri + REST_REPORT_STATUS; return new UriTemplate(fullUri).expand(executionId); } /** * Saves resource ouput in file. * * @param executionId Identifies current id of running report. * @param exportOutput Identifier which refers to current requested export. * @param file The file in which the output will be saved * @throws RestClientException */ public void saveExportOutputToFile(String executionId, String exportOutput, File file) throws RestClientException { URI outputResourceUri = getExportOutputResourceURI(executionId, exportOutput); downloadFile(outputResourceUri, file); } /** * Load output data for export on current request. * * @param executionId Identifies current id of running report. * @param exportOutput Identifier which refers to current requested export. * @return Basically it will be 3 cases HTML/JSON/XML types. */ public ReportDataResponse runExportOutputResource(String executionId, String exportOutput) { ResponseEntity<String> response = restTemplate.exchange( getExportOutputResourceURI(executionId, exportOutput), HttpMethod.GET, null, String.class); boolean isFinal; boolean hasOutputFinal = response.getHeaders().containsKey("output-final"); String data = response.getBody(); if (hasOutputFinal) { isFinal = Boolean.valueOf(response.getHeaders().getFirst("output-final")); } else { // "output-final" header is missing for JRS 5.5 and lower, // so we consider request to be not final by default isFinal = false; } return new ReportDataResponse(isFinal, data); } /** * Generates link for requesting report output resource datum. * * @param executionId Identifies current id of running report. * @param exportOutput Identifier which refers to current requested export. * @return "{server url}/rest_v2/reportExecutions/{executionId}/exports/{exportOutput}/outputResource" */ public URI getExportOutputResourceURI(String executionId, String exportOutput) { String outputResourceUri = "/{executionId}/exports/{exportOutput}/outputResource"; String fullUri = jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_REPORT_EXECUTIONS + outputResourceUri; UriTemplate uriTemplate = new UriTemplate(fullUri); return uriTemplate.expand(executionId, exportOutput); } /** * Save report in file with specified name. * * @param executionId Identifies current id of running report. * @param exportOutput Identifier which refers to current requested export. * @param attachmentName Name of attachment we store on JRS side. * @param file The file in which the output will be saved * @throws RestClientException */ public void saveExportAttachmentToFile(String executionId, String exportOutput, String attachmentName, File file) throws RestClientException { URI attachmentUri = getExportAttachmentURI(executionId, exportOutput, attachmentName); downloadFile(attachmentUri, file); } /** * Generates link for requesting of report export attachemnt data. * * @param executionId Identifies current id of running report. * @param exportOutput Identifier which refers to current requested export. * @param attachmentName Name of attachment we store on JRS side. * @return "{server url}/rest_v2/reportExecutions/{executionId}/exports/{exportOutput}/attachments/{attachment}" */ public URI getExportAttachmentURI(String executionId, String exportOutput, String attachmentName) { String attachmentUri = "/{executionId}/exports/{exportOutput}/attachments/{attachment}"; String fullUri = jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_REPORT_EXECUTIONS + attachmentUri; return new UriTemplate(fullUri).expand(executionId, exportOutput, attachmentName); } /** * Sends request for retrieving report details data. * * @param executionId Identifies current id of running report. * @return response which expose current report details. */ public ReportExecutionResponse runReportDetailsRequest(String executionId) { return restTemplate.getForObject(getReportDetailsURI(executionId), ReportExecutionResponse.class); } /** * Generates link for requesting details on specified report. * * @param executionId Identifies current id of running report. * @return "{server url}/rest_v2/reportExecutions/{executionId}" */ public URI getReportDetailsURI(String executionId) { String attachmentUri = "/{executionId}"; String fullUri = jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_REPORT_EXECUTIONS + attachmentUri; return new UriTemplate(fullUri).expand(executionId); } //--------------------------------------------------------------------- // Input Controls //--------------------------------------------------------------------- /** * Gets the resource descriptor of a query-based input control that contains query data * according to specified parameters. * * @param uri repository URI of the input control * @param datasourceUri repository URI of a datasource for the control * @param params parameters for the input control (can be <code>null</code>) * @return the ResourceDescriptor of a query-based input control that contains query data * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public ResourceDescriptor getInputControlWithQueryData(String uri, String datasourceUri, List<ResourceParameter> params) throws RestClientException { StringBuilder fullUri = new StringBuilder(); fullUri.append(restServicesUrl).append(REST_RESOURCE_URI).append(uri); fullUri.append("?IC_GET_QUERY_DATA=").append(datasourceUri); if (params != null) { for (ResourceParameter parameter : params) { fullUri.append(parameter.isListItem() ? "&PL_" : "&P_"); fullUri.append(parameter.getName()).append("=").append(parameter.getValue()); } } return restTemplate.getForObject(fullUri.toString(), ResourceDescriptor.class); } /** * Gets the query data of a query-based input control, according to specified parameters. * * @param uri repository URI of the input control * @param datasourceUri repository URI of a datasource for the control * @param params parameters for the input control (can be <code>null</code>) * @return The query data as list of ResourceProperty objects * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors */ public List<ResourceProperty> getInputControlQueryData(String uri, String datasourceUri, List<ResourceParameter> params) throws RestClientException { ResourceDescriptor descriptor = getInputControlWithQueryData(uri, datasourceUri, params); ResourceProperty queryDataProperty = descriptor.getPropertyByName(ResourceDescriptor.PROP_QUERY_DATA); List<ResourceProperty> listOfValues = new ArrayList<ResourceProperty>(); if (queryDataProperty != null) { List<ResourceProperty> queryData = queryDataProperty.getProperties(); // rows for (ResourceProperty queryDataRow : queryData) { ResourceProperty property = new ResourceProperty(); property.setName(queryDataRow.getValue()); //cols StringBuilder value = new StringBuilder(); for (ResourceProperty queryDataCol : queryDataRow.getProperties()) { if (ResourceDescriptor.PROP_QUERY_DATA_ROW_COLUMN.equals(queryDataCol.getName())) { if (value.length() > 0) value.append(" | "); value.append(queryDataCol.getValue()); } } property.setValue(value.toString()); listOfValues.add(property); } } return listOfValues; } //--------------------------------------------------------------------- // Input Controls v2 //--------------------------------------------------------------------- /** * Gets the list of all input controls for the report with specified URI. * * @param reportUri repository URI of the report * @return a list of input controls * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public List<InputControl> getInputControls(String reportUri) throws RestClientException { return getInputControlsList(reportUri).getInputControls(); } /** * Gets the list of input controls with specified IDs for the report with specified URI * and according to selected values. * * @param reportUri repository URI of the report * @param controlsIds list of input controls IDs * @param selectedValues list of selected values * @return a list of input controls * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public List<InputControl> getInputControls(String reportUri, List<String> controlsIds, List<ReportParameter> selectedValues) throws RestClientException { return getInputControlsList(reportUri, controlsIds, selectedValues).getInputControls(); } /** * Gets the list of all input controls for the report with specified URI. * * @param reportUri repository URI of the report * @return the InputControlsList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public InputControlsList getInputControlsList(String reportUri) throws RestClientException { return getInputControlsList(reportUri, new ArrayList<String>(), new ArrayList<ReportParameter>()); } /** * Gets the list of input controls with specified IDs for the report with specified URI * and according to selected values. * * @param reportUri repository URI of the report * @param controlsIds list of input controls IDs * @param selectedValues list of selected values * @return the InputControlsList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public InputControlsList getInputControlsList(String reportUri, List<String> controlsIds, List<ReportParameter> selectedValues) throws RestClientException { // generate full url String url = generateInputControlsUrl(reportUri, controlsIds, false); // add selected values to request ReportParametersList parametersList = new ReportParametersList(); parametersList.setReportParameters(selectedValues); // execute POST request InputControlsList controlsList = restTemplate.postForObject(url, parametersList, InputControlsList.class); return (controlsList != null) ? controlsList : new InputControlsList(); } /** * Gets the list of states of all input controls for the report with specified URI. * * @param reportUri repository URI of the report * @return the list of the input controls states * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public List<InputControlState> getInputControlsValues(String reportUri) throws RestClientException { return getInputControlsValuesList(reportUri).getInputControlStates(); } /** * Gets the list of states of input controls with specified IDs for the report with specified URI * and according to selected values. * * @param reportUri repository URI of the report * @param controlsIds list of input controls IDs * @param selectedValues list of selected values * @return the list of the input controls states * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public List<InputControlState> getInputControlsValues(String reportUri, List<String> controlsIds, List<ReportParameter> selectedValues) throws RestClientException { return getInputControlsValuesList(reportUri, controlsIds, selectedValues).getInputControlStates(); } /** * Gets the list of states of all input controls for the report with specified URI. * * @param reportUri repository URI of the report * @return the InputControlStatesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public InputControlStatesList getInputControlsValuesList(String reportUri) throws RestClientException { return getInputControlsValuesList(reportUri, new ArrayList<String>(), new ArrayList<ReportParameter>()); } /** * Gets the list of states of input controls with specified IDs for the report with specified URI * and according to selected values. * * @param reportUri repository URI of the report * @param controlsIds list of input controls IDs * @param selectedValues list of selected values * @return the InputControlStatesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public InputControlStatesList getInputControlsValuesList(String reportUri, List<String> controlsIds, List<ReportParameter> selectedValues) throws RestClientException { // generate full url String url = generateInputControlsUrl(reportUri, controlsIds, true); // add selected values to request ReportParametersList parametersList = new ReportParametersList(); parametersList.setReportParameters(selectedValues); // execute POST request try { return restTemplate.postForObject(url, parametersList, InputControlStatesList.class); } catch (HttpMessageNotReadableException exception) { return new InputControlStatesList(); } } /** * Validates the input controls values on the server side and returns states only for invalid controls. * * @param reportUri repository URI of the report * @param inputControls list of input controls that should be validated * @return the list of the input controls states * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.4 */ public List<InputControlState> validateInputControlsValues(String reportUri, List<InputControl> inputControls) throws RestClientException { return validateInputControlsValuesList(reportUri, inputControls).getInputControlStates(); } /** * Validates the input controls values on the server side and returns states only for invalid controls. * * @param reportUri repository URI of the report * @param controlsIds list of input controls IDs that should be validated * @param selectedValues list of selected values for validation * @return the list of the input controls states * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public List<InputControlState> validateInputControlsValues(String reportUri, List<String> controlsIds, List<ReportParameter> selectedValues) throws RestClientException { return validateInputControlsValuesList(reportUri, controlsIds, selectedValues).getInputControlStates(); } /** * Validates the input controls values on the server side and returns states only for invalid controls. * * @param reportUri repository URI of the report * @param inputControls list of input controls that should be validated * @return the InputControlStatesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public InputControlStatesList validateInputControlsValuesList(String reportUri, List<InputControl> inputControls) throws RestClientException { List<String> controlsIds = new ArrayList<String>(); List<ReportParameter> selectedValues = new ArrayList<ReportParameter>(); for (InputControl control : inputControls) { controlsIds.add(control.getId()); selectedValues.add(new ReportParameter(control.getId(), control.getSelectedValues())); } // execute validation request return validateInputControlsValuesList(reportUri, controlsIds, selectedValues); } /** * Validates the input controls values on the server side and returns states only for invalid controls. * * @param reportUri repository URI of the report * @param controlsIds list of input controls IDs that should be validated * @param selectedValues list of selected values for validation * @return the InputControlStatesList value * @throws RestClientException thrown by RestTemplate whenever it encounters client-side HTTP errors * @since 1.6 */ public InputControlStatesList validateInputControlsValuesList(String reportUri, List<String> controlsIds, List<ReportParameter> selectedValues) throws RestClientException { InputControlStatesList statesList = getInputControlsValuesList(reportUri, controlsIds, selectedValues); // remove states without validation errors Iterator<InputControlState> iterator = statesList.getInputControlStates().iterator(); while (iterator.hasNext()) { if (iterator.next().getError() == null) { iterator.remove(); } } return statesList; } /** * Method which flashes all stared cookies. */ public static void flushCookies() { StaticCacheHelper.clearCache(); } //--------------------------------------------------------------------- // Thumbnails API //--------------------------------------------------------------------- /** * Returns thumbnail image or encoded image of the requested URI without placeholder * * @param resourceUri Uri of resource * @return {serverUrl}/rest_v2/thumbnails/{resourceUri}?defaultAllowed=false */ public String generateThumbNailUri(String resourceUri) { return generateThumbNailUri(resourceUri, false); } /** * Returns thumbnail image or encoded image of the requested URI * * @param resourceUri Uri of resource * @param defaultAllowed If true, a placeholder thumbnail will be provided when no thumbnail is available (default: false) * @return {serverUrl}/rest_v2/thumbnails/{resourceUri}?defaultAllowed={allowedFlag} */ public String generateThumbNailUri(String resourceUri, boolean defaultAllowed) { return jsServerProfile.getServerUrl() + REST_SERVICES_V2_URI + REST_THUMBNAILS + resourceUri + "?defaultAllowed=" + Boolean.toString(defaultAllowed); } //--------------------------------------------------------------------- // Helper methods //--------------------------------------------------------------------- private void downloadFile(URI uri, File file) throws RestClientException { ClientHttpResponse response = null; try { ClientHttpRequest request = restTemplate.getRequestFactory().createRequest(uri, HttpMethod.GET); response = request.execute(); if (restTemplate.getErrorHandler().hasError(response)) { restTemplate.getErrorHandler().handleError(response); } copyResponseToFile(response, file); } catch (IOException ex) { throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex); } finally { if (response != null) response.close(); } } protected int copyResponseToFile(ClientHttpResponse response, File file) throws IOException { File parentFolder = file.getParentFile(); if (parentFolder != null && !parentFolder.exists() && !parentFolder.mkdirs()) { throw new IllegalStateException("Unable to create folder: " + parentFolder); } return FileCopyUtils.copy(response.getBody(), new FileOutputStream(file)); } private void updateRequestFactoryTimeouts() { updateConnectTimeout(); updateReadTimeout(); } private void updateConnectTimeout() { requestFactory.setConnectTimeout(connectTimeout); } private void updateReadTimeout() { requestFactory.setReadTimeout(readTimeout); } private String generateInputControlsUrl(String reportUri, List<String> controlsIds, boolean valuesOnly) { StringBuilder fullUri = new StringBuilder(); fullUri.append(jsServerProfile.getServerUrl()).append(REST_SERVICES_V2_URI).append(REST_REPORTS_URI) .append(reportUri).append(REST_INPUT_CONTROLS_URI); // add ids to uri if (!controlsIds.isEmpty()) { fullUri.append("/"); for (String id : controlsIds) { fullUri.append(id).append(";"); } } // add "values" suffix if (valuesOnly) { fullUri.append(REST_VALUES_URI); } return fullUri.toString(); } private void fetchXmlConverter() { List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters(); for (HttpMessageConverter<?> converter : converters) { if (converter instanceof SimpleXmlHttpMessageConverter) { simpleXmlHttpMessageConverter = (SimpleXmlHttpMessageConverter) converter; } } } private void configureAnnotationStrategy() { if (simpleXmlHttpMessageConverter != null) { Strategy annotationStrategy = new AnnotationStrategy(); Serializer serializer = new Persister(annotationStrategy); simpleXmlHttpMessageConverter.setSerializer(serializer); } } //--------------------------------------------------------------------- // Getters & Setters //--------------------------------------------------------------------- public RestTemplate getRestTemplate() { return restTemplate; } public String getRestServicesUrl() { return restServicesUrl; } }