All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.belladati.sdk.impl.BellaDatiServiceImpl Maven / Gradle / Ivy

Go to download

The BellaDati SDK allows accessing a BellaDati server from 3rd-party applications using Java. This project contains the implementation for standard Java.

The newest version!
package com.belladati.sdk.impl;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.imageio.ImageIO;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.message.BasicNameValuePair;

import com.belladati.sdk.BellaDatiService;
import com.belladati.sdk.dashboard.Dashboard;
import com.belladati.sdk.dashboard.DashboardInfo;
import com.belladati.sdk.dataset.AttributeValue;
import com.belladati.sdk.dataset.DataSet;
import com.belladati.sdk.dataset.DataSetInfo;
import com.belladati.sdk.dataset.data.DataRow;
import com.belladati.sdk.dataset.data.DataTable;
import com.belladati.sdk.dataset.source.DataSource;
import com.belladati.sdk.dataset.source.DataSourceImport;
import com.belladati.sdk.dataset.source.DataSourcePendingImport;
import com.belladati.sdk.exception.InternalConfigurationException;
import com.belladati.sdk.exception.dataset.data.UnknownServerColumnException;
import com.belladati.sdk.exception.server.NotFoundException;
import com.belladati.sdk.exception.server.UnexpectedResponseException;
import com.belladati.sdk.filter.Filter;
import com.belladati.sdk.impl.AttributeValueImpl.InvalidAttributeValueException;
import com.belladati.sdk.impl.DataSourceImportImpl.InvalidDataSourceImportException;
import com.belladati.sdk.intervals.DateUnit;
import com.belladati.sdk.intervals.Interval;
import com.belladati.sdk.intervals.TimeUnit;
import com.belladati.sdk.report.Comment;
import com.belladati.sdk.report.Report;
import com.belladati.sdk.report.ReportInfo;
import com.belladati.sdk.user.User;
import com.belladati.sdk.util.CachedList;
import com.belladati.sdk.util.PaginatedIdList;
import com.belladati.sdk.util.PaginatedList;
import com.belladati.sdk.view.ViewLoader;
import com.belladati.sdk.view.ViewType;
import com.belladati.sdk.view.export.ViewExporter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

class BellaDatiServiceImpl implements BellaDatiService {

	/** The serialVersionUID */
	private static final long serialVersionUID = 9054278401541000710L;

	public static final String DATE_TIME_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";

	final BellaDatiClient client;
	final TokenHolder tokenHolder;

	private final transient PaginatedIdList dashboardList = new DashboardList();

	private final transient PaginatedIdList reportList = new ReportList();

	private final transient PaginatedIdList dataSetList = new DataSetList();

	private final transient Map> commentLists = Collections
		.synchronizedMap(new HashMap>());

	private final transient Map>> dataSetAttributeValues = new HashMap>>();

	private final transient Map> dataSourceList = new HashMap>();

	private final transient Map> dataSourceImportList = new HashMap>();

	BellaDatiServiceImpl(BellaDatiClient client, TokenHolder tokenHolder) {
		this.client = client;
		this.tokenHolder = tokenHolder;
	}

	@Override
	public PaginatedIdList getDashboardInfo() {
		return dashboardList;
	}

	@Override
	public Dashboard loadDashboard(String id) {
		return new DashboardImpl(this, loadJson("api/dashboards/" + id));
	}

	@Override
	public Object loadDashboardThumbnail(String id) throws IOException {
		return loadImage("api/dashboards/" + id + "/thumbnail");
	}

	@Override
	public PaginatedIdList getReportInfo() {
		return reportList;
	}

	@Override
	public Report loadReport(String id) {
		return new ReportImpl(this, loadJson("api/reports/" + id));
	}

	@Override
	public Object loadReportThumbnail(String reportId) throws IOException {
		return loadImage("api/reports/" + reportId + "/thumbnail");
	}

	/**
	 * Convenience method for other parts of the implementation to easily load
	 * data through the API.
	 * 
	 * @param uri URI to contact
	 * @return the resulting JSON response
	 */
	JsonNode loadJson(String uri) {
		return client.getJson(uri, tokenHolder);
	}

	/**
	 * Loads an image from the given URI.
	 * 
	 * @param relativeUrl the URI to load from
	 * @return the image from the server
	 * @throws IOException if the image cannot be loaded
	 */
	Object loadImage(String relativeUrl) throws IOException {
		ByteArrayInputStream bais = new ByteArrayInputStream(client.get(relativeUrl, tokenHolder));
		try {
			BufferedImage image = ImageIO.read(bais);
			if (image == null) {
				throw new IOException("Could not load image");
			}
			return image;
		} finally {
			bais.close();
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @return {@link ByteArrayInputStream}
	 */
	@Override
	public Object loadFile(String relativeUrl) throws IOException {
		return new ByteArrayInputStream(client.get(relativeUrl, tokenHolder));
	}

	@Override
	public PaginatedList getReportComments(String reportId) {
		PaginatedList existing = commentLists.get(reportId);
		if (existing != null) {
			return existing;
		} else {
			synchronized (commentLists) {
				existing = commentLists.get(reportId);
				if (existing != null) {
					return existing;
				} else {
					PaginatedList newList = new PaginatedListImpl(this, "api/reports/" + reportId + "/comments",
						"comments") {
						@Override
						protected Comment parse(BellaDatiServiceImpl service, JsonNode node) {
							return new CommentImpl(service, node);
						}
					};
					commentLists.put(reportId, newList);
					return newList;
				}
			}
		}
	}

	@Override
	public void postComment(String reportId, String text) {
		client.post("api/reports/" + reportId + "/comments", tokenHolder,
			Collections.singletonList(new BasicNameValuePair("text", text)));
	}

	@Override
	public Object loadViewContent(String viewId, ViewType viewType, Filter... filters) {
		return loadViewContent(viewId, viewType, Arrays.asList(filters));
	}

	@Override
	public Object loadViewContent(String viewId, ViewType viewType, Collection> filters) {
		return createViewLoader(viewId, viewType).addFilters(filters).loadContent();
	}

	@Override
	public ViewLoader createViewLoader(String viewId, ViewType viewType) {
		return new ViewLoaderImpl(this, viewId, viewType);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ViewExporter createViewExporter() {
		return new ViewExporterImpl(this);
	}

	@Override
	public CachedList getDataSources(String id) throws NotFoundException {
		CachedListImpl list = dataSourceList.get(id);
		if (list == null) {
			// we don't have this data set's sources in our cache yet
			list = new CachedListImpl(this, "api/dataSets/" + id + "/dataSources", "dataSources") {
				@Override
				protected DataSource parse(BellaDatiServiceImpl service, JsonNode node) throws ParseException {
					return new DataSourceImpl(service, node);
				}
			};
			dataSourceList.put(id, list);
		}
		return list;
	}

	@Override
	public CachedList getDataSourceImports(String id) throws NotFoundException {
		CachedListImpl list = dataSourceImportList.get(id);
		if (list == null) {
			// we don't have this data set's sources in our cache yet
			list = new CachedListImpl(this, "api/dataSets/dataSources/" + id + "/executions", "executions") {
				@Override
				protected DataSourceImport parse(BellaDatiServiceImpl service, JsonNode node) throws ParseException {
					try {
						return new DataSourceImportImpl(node);
					} catch (InvalidDataSourceImportException e) {
						throw new ParseException(node, e);
					}
				}
			};
			dataSourceImportList.put(id, list);
		}
		return list;
	}

	@Override
	public DataSourcePendingImport setupDataSourceImport(String id, Date date) {
		return new DataSourcePendingImportImpl(this, id, date);
	}

	/**
	 * Appends a filter parameter from the given filters to the URI builder.
	 * Won't do anything if the filter collection is empty.
	 * 
	 * @param builder the builder to append to
	 * @param filters filters to append
	 * @return the same builder, for chaining
	 */
	URIBuilder appendFilter(URIBuilder builder, Collection> filters) {
		if (filters.size() > 0) {
			ObjectNode filterNode = new ObjectMapper().createObjectNode();
			for (Filter filter : filters) {
				filterNode.setAll(filter.toJson());
			}
			ObjectNode drilldownNode = new ObjectMapper().createObjectNode();
			drilldownNode.put("drilldown", filterNode);
			builder.addParameter("filter", drilldownNode.toString());
		}
		return builder;
	}

	/**
	 * Appends a date/time definition parameter to the URI builder. Won't do
	 * anything if both intervals are null.
	 * 
	 * @param builder the builder to append to
	 * @param dateInterval date interval to append, or null
	 * @param timeInterval time interval to append, or null
	 * @return the same builder, for chaining
	 */
	URIBuilder appendDateTime(URIBuilder builder, Interval dateInterval, Interval timeInterval) {
		if (dateInterval != null || timeInterval != null) {
			ObjectNode dateTimeNode = new ObjectMapper().createObjectNode();
			if (dateInterval != null) {
				dateTimeNode.setAll(dateInterval.toJson());
			}
			if (timeInterval != null) {
				dateTimeNode.setAll(timeInterval.toJson());
			}
			builder.addParameter("dateTimeDefinition", dateTimeNode.toString());
		}
		return builder;
	}

	/**
	 * Appends a locale language parameter to the URI builder. Won't do anything
	 * if the locale is null.
	 * 
	 * @param builder the builder to append to
	 * @param locale the locale to append
	 * @return the same builder, for chaining
	 */
	URIBuilder appendLocale(URIBuilder builder, Locale locale) {
		if (locale != null) {
			builder.addParameter("lang", locale.getLanguage());
		}
		return builder;
	}

	@Override
	public synchronized CachedList getAttributeValues(String dataSetId, String attributeCode) {
		if (!dataSetAttributeValues.containsKey(dataSetId)) {
			// we don't have any values for this report yet, insert new map
			dataSetAttributeValues.put(dataSetId, new HashMap>());
		}

		Map> attributeValues = dataSetAttributeValues.get(dataSetId);

		CachedListImpl values = attributeValues.get(attributeCode);
		if (values == null) {
			// we don't have this attribute in our cache yet
			values = new CachedListImpl(this,
				"api/dataSets/" + dataSetId + "/attributes/" + attributeCode + "/values", "values") {
				@Override
				protected AttributeValue parse(BellaDatiServiceImpl service, JsonNode node) throws ParseException {
					try {
						return new AttributeValueImpl(node);
					} catch (InvalidAttributeValueException e) {
						throw new ParseException(node, e);
					}
				}
			};
			attributeValues.put(attributeCode, values);
		}
		return values;
	}

	@Override
	public User loadUser(String userId) {
		return new UserImpl(loadJson("api/users/" + userId));
	}

	@Override
	public Object loadUserImage(String userId) throws IOException {
		return loadImage("api/users/" + userId + "/image");
	}

	@Override
	public String toString() {
		return "BellaDati Service(server: " + client.getBaseUrl() + ", key: " + tokenHolder.getConsumerKey() + ", token: "
			+ tokenHolder.getToken() + ")";
	}

	@Override
	public PaginatedIdList getDataSetInfo() {
		return dataSetList;
	}

	@Override
	public DataSet loadDataSet(String id) throws NotFoundException {
		return new DataSetImpl(this, loadJson("api/dataSets/" + id));
	}

	@Override
	public void uploadData(String id, DataTable data) {
		List rows = data.getRows();
		if (rows.size() == 0) {
			// if we don't have data, do nothing
			return;
		}
		try {
			client.postUpload("api/import/" + id, tokenHolder, data.toJson().toString());
		} catch (UnexpectedResponseException e) {
			if (e.getResponseCode() == 400) {
				Pattern codePattern = Pattern.compile(".*?'(.*?)'.*");
				Matcher codeMatcher = codePattern.matcher(e.getResponseContent());
				if (codeMatcher.matches()) {
					throw new UnknownServerColumnException(id, codeMatcher.group(1));
				}
			}
			throw new UnexpectedResponseException(e.getResponseCode(), e.getResponseContent(), e);
		}
	}

	/** Deserialization. Sets up the element lists and maps as empty objects. */
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
		in.defaultReadObject();
		try {
			Field dashboardList = getClass().getDeclaredField("dashboardList");
			dashboardList.setAccessible(true);
			dashboardList.set(this, new DashboardList());

			Field reportList = getClass().getDeclaredField("reportList");
			reportList.setAccessible(true);
			reportList.set(this, new ReportList());

			Field dataSetList = getClass().getDeclaredField("dataSetList");
			dataSetList.setAccessible(true);
			dataSetList.set(this, new DataSetList());

			Field commentLists = getClass().getDeclaredField("commentLists");
			commentLists.setAccessible(true);
			commentLists.set(this, Collections.synchronizedMap(new HashMap>()));

			Field reportAttributeValues = getClass().getDeclaredField("dataSetAttributeValues");
			reportAttributeValues.setAccessible(true);
			reportAttributeValues.set(this, new HashMap>>());

			Field dataSourceList = getClass().getDeclaredField("dataSourceList");
			dataSourceList.setAccessible(true);
			dataSourceList.set(this, new HashMap>());

			Field dataSourceImportList = getClass().getDeclaredField("dataSourceImportList");
			dataSourceImportList.setAccessible(true);
			dataSourceImportList.set(this, new HashMap>());
		} catch (NoSuchFieldException e) {
			throw new InternalConfigurationException("Failed to set service fields", e);
		} catch (IllegalAccessException e) {
			throw new InternalConfigurationException("Failed to set service fields", e);
		} catch (SecurityException e) {
			throw new InternalConfigurationException("Failed to set service fields", e);
		} catch (IllegalArgumentException e) {
			throw new InternalConfigurationException("Failed to set service fields", e);
		}
	}

	/** Paginated list class for dashboards. */
	private class DashboardList extends PaginatedIdListImpl {
		public DashboardList() {
			super(BellaDatiServiceImpl.this, "api/dashboards", "dashboards");
		}

		@Override
		protected DashboardInfo parse(BellaDatiServiceImpl service, JsonNode node) {
			return new DashboardInfoImpl(service, node);
		}
	}

	/** Paginated list class for reports. */
	private class ReportList extends PaginatedIdListImpl {
		public ReportList() {
			super(BellaDatiServiceImpl.this, "api/reports", "reports");
		}

		@Override
		protected ReportInfo parse(BellaDatiServiceImpl service, JsonNode node) {
			return new ReportInfoImpl(service, node);
		}
	}

	/** Paginated list class for data sets. */
	private class DataSetList extends PaginatedIdListImpl {
		public DataSetList() {
			super(BellaDatiServiceImpl.this, "api/dataSets", "dataSets");
		}

		@Override
		protected DataSetInfo parse(BellaDatiServiceImpl service, JsonNode node) {
			return new DataSetInfoImpl(service, node);
		}
	}

	@Override
	public byte[] post(String uri) throws URISyntaxException {
		return post(uri, Collections. emptyMap());
	}

	@Override
	public byte[] post(String uri, Map uriParameters) throws URISyntaxException {
		return postForm(uri, uriParameters, Collections. emptyMap());
	}

	@Override
	public byte[] post(String uri, byte[] content) throws URISyntaxException {
		return post(uri, Collections. emptyMap(), content);
	}

	@Override
	public byte[] post(String uri, Map uriParameters, byte[] content) throws URISyntaxException {
		URIBuilder builder = new URIBuilder(uri);
		for (Entry entry : uriParameters.entrySet()) {
			builder.addParameter(entry.getKey(), entry.getValue());
		}
		return client.postData(builder.build().toString(), tokenHolder, content);
	}

	@Override
	public byte[] postForm(String uri, Map formParameters) throws URISyntaxException {
		return postForm(uri, Collections. emptyMap(), formParameters);
	}

	@Override
	public byte[] postForm(String uri, Map uriParameters, Map formParameters)
		throws URISyntaxException {
		URIBuilder builder = new URIBuilder(uri);
		for (Entry entry : uriParameters.entrySet()) {
			builder.addParameter(entry.getKey(), entry.getValue());
		}
		List formParams = new ArrayList();
		for (Entry entry : formParameters.entrySet()) {
			formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		}
		return client.post(builder.build().toString(), tokenHolder, formParams);
	}

	@Override
	public byte[] get(String uri) throws URISyntaxException {
		return get(uri, Collections. emptyMap());
	}

	@Override
	public byte[] get(String uri, Map uriParameters) throws URISyntaxException {
		URIBuilder builder = new URIBuilder(uri);
		for (Entry entry : uriParameters.entrySet()) {
			builder.addParameter(entry.getKey(), entry.getValue());
		}
		return client.get(builder.build().toString(), tokenHolder);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy