/**
*
*/
package com.thinkbiganalytics.metadata.rest.client;
/*-
* #%L
* thinkbig-metadata-rest-client-spring
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* 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.
* #L%
*/
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.thinkbiganalytics.metadata.api.op.FeedDependencyDeltaResults;
import com.thinkbiganalytics.metadata.rest.model.data.Datasource;
import com.thinkbiganalytics.metadata.rest.model.data.DatasourceCriteria;
import com.thinkbiganalytics.metadata.rest.model.data.DirectoryDatasource;
import com.thinkbiganalytics.metadata.rest.model.data.HiveTableColumn;
import com.thinkbiganalytics.metadata.rest.model.data.HiveTableDatasource;
import com.thinkbiganalytics.metadata.rest.model.data.HiveTablePartition;
import com.thinkbiganalytics.metadata.rest.model.extension.ExtensibleTypeDescriptor;
import com.thinkbiganalytics.metadata.rest.model.feed.Feed;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedCategory;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedCriteria;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedDependencyGraph;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedPrecondition;
import com.thinkbiganalytics.metadata.rest.model.feed.InitializationStatus;
import com.thinkbiganalytics.metadata.rest.model.nifi.NiFiFlowCacheSync;
import com.thinkbiganalytics.metadata.rest.model.op.DataOperation;
import com.thinkbiganalytics.metadata.rest.model.sla.ServiceLevelAgreement;
import com.thinkbiganalytics.metadata.rest.model.sla.ServiceLevelAssessment;
import com.thinkbiganalytics.metadata.sla.api.Metric;
import com.thinkbiganalytics.support.FeedNameUtil;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClients;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
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.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.net.ssl.SSLContext;
/**
* A client for accessing the metadata store
*/
public class MetadataClient {
public static final List<MediaType> ACCEPT_TYPES = Collections.unmodifiableList(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN));
public static final ParameterizedTypeReference<List<ExtensibleTypeDescriptor>> TYPE_LIST = new ParameterizedTypeReference<List<ExtensibleTypeDescriptor>>() {
};
public static final ParameterizedTypeReference<List<Feed>> FEED_LIST = new ParameterizedTypeReference<List<Feed>>() {
};
public static final ParameterizedTypeReference<List<Datasource>> DATASOURCE_LIST = new ParameterizedTypeReference<List<Datasource>>() {
};
private static final Logger log = LoggerFactory.getLogger(MetadataClient.class);
private static final Function<UriComponentsBuilder, UriComponentsBuilder> ALL_DATASOURCES = new TargetDatasourceCriteria();
private static final Function<UriComponentsBuilder, UriComponentsBuilder> ALL_FEEDS = new TargetFeedCriteria();
private final URI base;
/**
* The base uri to access anything under the /proxy folder for Kylo
*/
private final URI proxyBase;
private final RestTemplate template;
private String category;
/**
* constructor, creates a metadata client for the base uri given
*
* @param base the URI of the metadata server
*/
public MetadataClient(URI base) {
this(base, null, null, null);
}
/**
* constructor, creates a metadata client for the base uri given, using the sslContext provided
*
* @param base the URI of the metadata server
* @param sslContext the SSL context
*/
public MetadataClient(URI base, SSLContext sslContext) {
this(base, null, null, sslContext);
}
/**
* constructor, creates a metadata client for the base uri given and authenticates to the server with the username and password provided.
*
* @param base the URI of the metadata server
* @param username the username to access the server
* @param password the password of the user
*/
public MetadataClient(URI base, String username, String password) {
this(base, username, password, null);
}
/**
* constructor, creates a metadata client for the base uri given, using the sslContext provided and authenticates to the server
* with the username and password.
*
* @param base the URI of the metadata server
* @param username the username to access the server
* @param password the password of the user
* @param sslContext the SSL context
*/
public MetadataClient(URI base, String username, String password, SSLContext sslContext) {
this(base, createCredentialProvider(username, password), sslContext);
}
public MetadataClient(URI base, CredentialsProvider credsProvider, SSLContext sslContext) {
super();
this.base = base;
this.proxyBase = URI.create(this.base.getScheme()+"://"+this.base.getHost()+"/proxy");
if (credsProvider != null) {
HttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider)
.setSSLContext(sslContext != null ? sslContext : null)
.build();
ClientHttpRequestFactory reqFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
this.template = new RestTemplate(reqFactory);
} else {
this.template = new RestTemplate();
}
ObjectMapper mapper = createObjectMapper();
this.template.getMessageConverters().add(new MappingJackson2HttpMessageConverter(mapper));
}
/**
* creates a credentials provider
*
* @param username the user name f
* @param password the password
* @return a credentials provider
*/
public static CredentialsProvider createCredentialProvider(String username, String password) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
return credsProvider;
}
/**
* Converts one or more strings to a path, compatible with the OS representation of a path
*
* @param first The base path
* @param more Additional directories to join to create a path, if any
* @return The completed, OS compatible path
*/
public static Path path(String first, String... more) {
return Paths.get(first, more);
}
/**
* gets the extensible types from the metadata store
*
* @return a list of extensible type descriptors
*/
public List<ExtensibleTypeDescriptor> getExtensibleTypes() {
return get(path("extension", "type"), null, TYPE_LIST);
}
/**
* gets the extensible type descriptor from the metadata store
*
* @param nameOrId the name or ID of the descriptor
* @return the extensible type descriptor
*/
public ExtensibleTypeDescriptor getExtensibleType(String nameOrId) {
return get(path("extension", "type", nameOrId), ExtensibleTypeDescriptor.class);
}
/**
* Creates a FeedBuilder initialized with the name and category given
*
* @param categoryName the name of the category
* @param name the name of the feed
* @return a FeedBuilder used to further construct feed details
*/
public FeedBuilder buildFeed(String categoryName, String name) {
return new FeedBuilderImpl(categoryName, name);
}
/**
* get the named high water mark for the feed given by feedId
*
* @param feedId the id of the feed
* @param waterMarkName the name of the water mark
* @return a string option
*/
public Optional<String> getHighWaterMarkValue(String feedId, String waterMarkName) {
return optinal(() -> get(path("feed", feedId, "watermark", waterMarkName), String.class));
}
/**
* Update the named high water mar, for the feed given by feedId, with the given value
*
* @param feedId the id of the feed
* @param waterMarkName the name of the water mark
* @param value the new value for the high water mark
*/
public void updateHighWaterMarkValue(String feedId, String waterMarkName, String value) {
put(path("feed", feedId, "watermark", waterMarkName), value, MediaType.TEXT_PLAIN);
}
/**
* Find out if the feed given has been initialized
*
* @param feedId the id of the feed
* @return an InitializationStatus, which can be used to get the status
*/
public InitializationStatus getCurrentInitStatus(String feedId) {
return nullable(() -> get(Paths.get("feed", feedId, "initstatus"), InitializationStatus.class));
}
/**
* update the initialization status of the feed
*
* @param feedId the id of the feed
* @param status the status of the feed
*/
public void updateCurrentInitStatus(String feedId, InitializationStatus status) {
put(Paths.get("feed", feedId, "initstatus"), status, MediaType.APPLICATION_JSON);
}
/**
* adds a datasource, to serve as the source, to the feed
*
* @param feedId the id of the feed
* @param datasourceId the id of the datasource to be added
* @return the feed
*/
public Feed addSource(String feedId, String datasourceId) {
Form form = new Form();
form.add("datasourceId", datasourceId);
return post(path("feed", feedId, "source"), form, Feed.class);
}
/**
* adds a datasource, to serve as the destination, of the feed
*
* @param feedId the id of the feed
* @param datasourceId the id of the datasource to be added
* @return the feed
*/
public Feed addDestination(String feedId, String datasourceId) {
Form form = new Form();
form.add("datasourceId", datasourceId);
return post(path("feed", feedId, "destination"), form, Feed.class);
}
/**
* creates a SLA with the metadata server, returns the SLA as created
*
* @param sla the SLA to create
* @return the SLA as created by the server
*/
public ServiceLevelAgreement createSla(ServiceLevelAgreement sla) {
return post(path("sla"), sla, MediaType.APPLICATION_JSON, ServiceLevelAgreement.class);
}
/**
* makes a precondition for the feed given
*
* @param feedId the id of the feed
* @param metrics on or more metrics that represent the precondition
*/
public Feed setPrecondition(String feedId, Metric... metrics) {
return setPrecondition(feedId, Arrays.asList(metrics));
}
/**
* makes a precondition for the feed given
*
* @param feedId the id of the feed
* @param metrics on or more metrics that represent the precondition
*/
public Feed setPrecondition(String feedId, List<Metric> metrics) {
FeedPrecondition precond = new FeedPrecondition("Feed " + feedId + " Precondition", "", metrics);
return setPrecondition(feedId, precond);
}
/**
* makes a precondition for the feed given
*
* @param feedId the id of the feed
* @param precond the precondition
*/
public Feed setPrecondition(String feedId, FeedPrecondition precond) {
return post(path("feed", feedId, "precondition"), precond, MediaType.APPLICATION_JSON, Feed.class);
}
/**
* a factory method to creates a new feed criteria
*
* @return a feed criteria instance
*/
public FeedCriteria feedCriteria() {
return new TargetFeedCriteria();
}
/**
* gets the list of all feeds
*
* @return all feeds
*/
public List<Feed> getFeeds() {
return getFeeds((FeedCriteria) ALL_FEEDS);
}
/**
* gets a list of feeds matching the criteria given
*
* @param criteria the criteria of the feeds to return
* @return a list of feeds
*/
public List<Feed> getFeeds(FeedCriteria criteria) {
try {
return get(path("feed"), (TargetFeedCriteria) criteria, FEED_LIST);
} catch (ClassCastException e) {
throw new IllegalThreadStateException("Unknown criteria type: " + criteria.getClass());
}
}
/**
* get the feed matching the id given
*
* @param id the id of the feed
* @return the feed instance
*/
public Feed getFeed(String id) {
return get(path("feed", id), Feed.class);
}
/**
* get the feed matching the named feed in the category given
*
* @param category the name of the category
* @param feed the name of the feed
*/
public Feed getFeed(String category,String feed) {
return get(path("feeds","name",category+"."+feed), Feed.class);
}
/**
* get the feed
*/
public FeedDependencyGraph getFeedDependency(String id) {
return get(path("feed", id, "depfeeds"), FeedDependencyGraph.class);
}
public FeedDependencyDeltaResults getFeedDependencyDeltas(String feedId) {
return get(path("feed", feedId, "depfeeds", "delta"), FeedDependencyDeltaResults.class);
}
public Feed updateFeed(Feed feed) {
// Using POST here in since it behaves more like a PATCH than a PUT, and PATCH is not supported in jersey.
return post(path("feed", feed.getId()), feed, MediaType.APPLICATION_JSON, Feed.class);
}
/**
* Gets the properties of the specified feed.
*
* @param id the feed id
* @return the metadata properties
*/
public Properties getFeedProperties(@Nonnull final String id) {
return get(path("feed", id, "props"), Properties.class);
}
/**
* merge the properties given into the feed
*
* @param feedId the id of the feed
* @param props a list of properties to merge into the feed
* @return the properties of the feed after the merge
*/
public Properties mergeFeedProperties(String feedId, Properties props) {
return post(path("feed", feedId, "props"), props, MediaType.APPLICATION_JSON, Properties.class);
}
/**
* replace the properties of the feed with thos given in props
*
* @param feedId the id of the feed
* @param props a list of properties to merge into the feed
* @return the properties of the feed after the operation has succeeded
*/
public Properties replaceFeedProperties(String feedId, Properties props) {
return put(path("feed", feedId), props, MediaType.APPLICATION_JSON, Properties.class);
}
/**
* a factory to create a new object that acts as a DirectoryDatasourceBuilder initialized with the name given
*
* @param name the name to reference the directory data source by
* @return the directory data source builder
*/
public DirectoryDatasourceBuilder buildDirectoryDatasource(String name) {
return new DirectoryDatasourceBuilderImpl(name);
}
/**
* a factory to create a new object that acts as a HiveTableDatasourceBuilder initialized with the name given
*
* @param name the name to reference the hive table data source by
* @return the hive table source builder
*/
public HiveTableDatasourceBuilder buildHiveTableDatasource(String name) {
return new HiveTableDatasourceBuilderImpl(name);
}
/**
* a factory method to create a new data source criteria
*
* @return a data source criteria model object
*/
public DatasourceCriteria datasourceCriteria() {
return new TargetDatasourceCriteria();
}
/**
* get the data sources available in the system
*
* @return the list of datasources
*/
public List<Datasource> getDatasources() {
return get(path("datasource"), ALL_DATASOURCES, DATASOURCE_LIST);
}
/**
* get the data sources available in the system, that match the criteria given
*
* @return the list of datasources
*/
public List<Datasource> getDatasources(DatasourceCriteria criteria) {
try {
return get(path("datasource"), (TargetDatasourceCriteria) criteria, DATASOURCE_LIST);
} catch (ClassCastException e) {
throw new IllegalThreadStateException("Unknown criteria type: " + criteria.getClass());
}
}
/**
* tells that an operation has begun, and allows us to set the status
*
* @param feedDestinationId the id of the destination feed
* @param status the status of the operation since begun
* @return the operation
*/
public DataOperation beginOperation(String feedDestinationId, String status) {
Form form = new Form();
form.add("feedDestinationId", feedDestinationId);
form.add("status", status);
return post(path("dataop"), form, DataOperation.class);
}
/**
* update the system with the info given in the operation. In other words the operation
* will be consulted and the metadata within will be persisted to the server.
*
* @param op the operation
* @return the operation, as recorded by the system
*/
public DataOperation updateDataOperation(DataOperation op) {
return put(path("dataop", op.getId()), op, MediaType.APPLICATION_JSON, DataOperation.class);
}
/**
* get the operation with the given id
*
* @param id the id of the operation
* @return the operation with the given id
*/
public DataOperation getDataOperation(String id) {
return get(path("dataop", id), DataOperation.class);
}
/**
* checks the pre-conditions of the feed with the given id and returns the result in an assessment object
*
* @param feedId the id of the feed
* @return the assessment of the feed, which can be consulted to check the state of the conditions for the SLA
*/
public ServiceLevelAssessment assessPrecondition(String feedId) {
return get(path("feed", feedId, "precondition", "assessment"), ServiceLevelAssessment.class);
}
/**
* get the results of the preconditions for the feed identified by id
*
* @param feedId the id of the feed
* @return a string representation of the assessment of the feed
*/
public String getPreconditionResult(String feedId) {
return get(path("feed", feedId, "precondition", "assessment", "result"), String.class);
}
private ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
// TODO Module dependency is causing a conflict somehow.
// mapper.registerModule(new JavaTimeModule());
mapper.setSerializationInclusion(Include.NON_NULL);
return mapper;
}
private <R> Optional<R> optinal(Supplier<R> supplier) {
try {
return Optional.ofNullable(supplier.get());
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
return Optional.empty();
} else {
throw e;
}
}
}
private <R> R nullable(Supplier<R> supplier) {
try {
return supplier.get();
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
return null;
} else {
throw e;
}
}
}
private FeedPrecondition createTrigger(List<Metric> metrics) {
if (!metrics.isEmpty()) {
FeedPrecondition trigger = new FeedPrecondition();
trigger.addMetrics("", metrics);
return trigger;
} else {
return null;
}
}
private Feed postFeed(Feed feed) {
return post(path("feed"), feed, MediaType.APPLICATION_JSON, Feed.class);
}
private HiveTableDatasource postDatasource(HiveTableDatasource ds) {
return post(path("datasource", "hivetable"), ds, MediaType.APPLICATION_JSON, HiveTableDatasource.class);
}
private DirectoryDatasource postDatasource(DirectoryDatasource ds) {
return post(path("datasource", "directory"), ds, MediaType.APPLICATION_JSON, DirectoryDatasource.class);
}
/**
* get flow updates since the last time we synchronized with NiFi
*
* @param syncId the synchronization id
* @return the cache of flow events
*/
public NiFiFlowCacheSync getFlowUpdates(String syncId) {
return get(path("nifi-provenance", "nifi-flow-cache", "get-flow-updates"), new NifiFlowSyncParameters(syncId), NiFiFlowCacheSync.class);
}
/**
* reset flow events for the given id
*
* @param syncId the synchronization id
* @return the cache of flow events after reset
*/
public NiFiFlowCacheSync resetFlowUpdates(String syncId) {
return get(path("nifi-provenance", "nifi-flow-cache", "reset-flow-updates"), new NifiFlowSyncParameters(syncId), NiFiFlowCacheSync.class);
}
/**
* finds the max event for NiFi
*
* @param clusterNodeId the NifI cluster to query
* @return the id of the most recent event
*/
public Long findNiFiMaxEventId(String clusterNodeId) {
log.info("findNifiMaxEventId ", clusterNodeId);
clusterNodeId = org.apache.commons.lang3.StringUtils.isBlank(clusterNodeId)?"NODE" : clusterNodeId;
return get(path("nifi-provenance", "max-event-id"), new MaxNifiEventParameters(clusterNodeId), Long.class);
}
/**
* reset the max event for NiFi
*
* @param clusterNodeId the NifI cluster to query
* @return the id of the most recent event
*/
public Long resetNiFiMaxEventId(String clusterNodeId) {
log.info("resetMaxEventId ", clusterNodeId);
clusterNodeId = org.apache.commons.lang3.StringUtils.isBlank(clusterNodeId)?"NODE" : clusterNodeId;
return post(path("nifi-provenance", "reset-max-event-id",clusterNodeId), null, Long.class);
}
/**
* queries to see if NiFi has new data flow events
*
* @return true if there new events, false otherwise
*/
public Boolean isNiFiFlowDataAvailable() {
return get(path("nifi-provenance", "nifi-flow-cache", "available"), Boolean.class);
}
/**
* Gets the data source with the specified id.
*
* @param id the data source id
* @return the data source, if found
* @throws RestClientException if the data source is unavailable
*/
public Optional<Datasource> getDatasource(@Nonnull final String id) {
try {
return Optional.of(get(path("datasource", id),
uri -> (uri != null) ? uri.queryParam("sensitive", true) : null,
Datasource.class));
} catch (final HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
return Optional.empty();
} else {
throw e;
}
}
}
private UriComponentsBuilder base(Path path) {
return UriComponentsBuilder.fromUri(this.base).path("/").path(path.toString());
}
private UriComponentsBuilder baseProxy(Path path) {
return UriComponentsBuilder.fromUri(this.proxyBase).path("/").path(path.toString());
}
private <R> R get(Path path, Class<R> resultType) {
return get(path, null, resultType);
}
private <R> R getProxy(Path path, Class<R> resultType) {
return getProxy(path, null, resultType);
}
private <R> R get(Path path, Function<UriComponentsBuilder, UriComponentsBuilder> filterFunct, Class<R> resultType) {
return this.template.getForObject(
(filterFunct != null ? filterFunct.apply(base(path)) : base(path)).build().toUri(),
resultType);
}
private <R> R getProxy(Path path, Function<UriComponentsBuilder, UriComponentsBuilder> filterFunct, Class<R> resultType) {
return this.template.getForObject(
(filterFunct != null ? filterFunct.apply(baseProxy(path)) : baseProxy(path)).build().toUri(),
resultType);
}
private <R> R get(Path path, ParameterizedTypeReference<R> responseEntity) {
return get(path, null, responseEntity);
}
private <R> R get(Path path, Function<UriComponentsBuilder, UriComponentsBuilder> filterFunct, ParameterizedTypeReference<R> responseEntity) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(ACCEPT_TYPES);
ResponseEntity<R> resp = this.template.exchange(
(filterFunct != null ? filterFunct.apply(base(path)) : base(path)).build().toUri(),
HttpMethod.GET,
new HttpEntity<Object>(headers),
responseEntity);
return handle(resp);
}
private <R> R post(Path path, Form form, Class<R> resultType) {
return this.template.postForObject(base(path).build().toUri(), form, resultType);
}
private <R> R post(Path path, Object body, MediaType mediaType, Class<R> resultType) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setAccept(ACCEPT_TYPES);
return this.template.postForObject(base(path).build().toUri(),
new HttpEntity<>(body, headers),
resultType);
}
private void put(Path path, Object body, MediaType mediaType) {
put(path, body, mediaType, null);
}
private <R> R put(Path path, Object body, MediaType mediaType, Class<R> resultType) {
URI uri = base(path).build().toUri();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setAccept(ACCEPT_TYPES);
this.template.put(uri, new HttpEntity<>(body, headers));
// Silly that put() doesn't return an object.
if (resultType != null) {
return get(path, resultType);
} else {
return null;
}
}
private <R> R handle(ResponseEntity<R> resp) {
if (resp.getStatusCode().is2xxSuccessful()) {
return resp.getBody();
} else {
throw new WebResponseException(ResponseEntity.status(resp.getStatusCode()).headers(resp.getHeaders()).build());
}
}
private static class Form extends LinkedMultiValueMap<String, String> {
}
private static class MaxNifiEventParameters implements Function<UriComponentsBuilder, UriComponentsBuilder> {
private String clusterNodeId;
public MaxNifiEventParameters(String clusterNodeId) {
this.clusterNodeId = clusterNodeId;
}
public UriComponentsBuilder apply(UriComponentsBuilder target) {
UriComponentsBuilder result = target;
if (!Strings.isNullOrEmpty(this.clusterNodeId)) {
result = result.queryParam("clusterNodeId", this.clusterNodeId);
}
return result;
}
}
private static class NifiFlowSyncParameters implements Function<UriComponentsBuilder, UriComponentsBuilder> {
private String syncId;
public NifiFlowSyncParameters(String syncId) {
this.syncId = syncId;
}
public UriComponentsBuilder apply(UriComponentsBuilder target) {
UriComponentsBuilder result = target;
if (!Strings.isNullOrEmpty(this.syncId)) {
result = result.queryParam("syncId", this.syncId);
}
return result;
}
}
private static class TargetDatasourceCriteria implements DatasourceCriteria, Function<UriComponentsBuilder, UriComponentsBuilder> {
private String name;
private String owner;
private DateTime createdOn;
private DateTime createdAfter;
private DateTime createdBefore;
private Set<String> types = new HashSet<>();
public UriComponentsBuilder apply(UriComponentsBuilder target) {
UriComponentsBuilder result = target;
if (!Strings.isNullOrEmpty(this.name)) {
result = result.queryParam(NAME, this.name);
}
if (!Strings.isNullOrEmpty(this.owner)) {
result = result.queryParam(OWNER, this.owner);
}
if (this.createdOn != null) {
result = result.queryParam(ON, this.createdOn.toString());
}
if (this.createdAfter != null) {
result = result.queryParam(AFTER, this.createdAfter.toString());
}
if (this.createdBefore != null) {
result = result.queryParam(BEFORE, this.createdBefore.toString());
}
if (!this.types.isEmpty()) {
result = result.queryParam(TYPE, types.toArray(new Object[types.size()]));
}
return result;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.DatasourceCriteria#name(java.lang.String)
*/
@Override
public DatasourceCriteria name(String name) {
this.name = name;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.DatasourceCriteria#createdOn(org.joda.time.DateTime)
*/
@Override
public DatasourceCriteria createdOn(DateTime time) {
this.createdOn = time;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.DatasourceCriteria#createdAfter(org.joda.time.DateTime)
*/
@Override
public DatasourceCriteria createdAfter(DateTime time) {
this.createdAfter = time;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.DatasourceCriteria#createdBefore(org.joda.time.DateTime)
*/
@Override
public DatasourceCriteria createdBefore(DateTime time) {
this.createdBefore = time;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.DatasourceCriteria#owner(java.lang.String)
*/
@Override
public DatasourceCriteria owner(String owner) {
this.owner = owner;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.DatasourceCriteria#type(java.lang.Class, java.lang.Class[])
*/
@Override
@SuppressWarnings("unchecked")
public DatasourceCriteria type(Class<? extends Datasource> type, Class<? extends Datasource>... others) {
this.types.add(type.getSimpleName());
for (Class<? extends Datasource> t : others) {
this.types.add(t.getSimpleName());
}
return this;
}
}
private static class TargetFeedCriteria implements FeedCriteria, Function<UriComponentsBuilder, UriComponentsBuilder> {
private String category;
private String name;
private String sourceId;
private String destinationId;
public UriComponentsBuilder apply(UriComponentsBuilder target) {
UriComponentsBuilder result = target;
if (!Strings.isNullOrEmpty(this.name)) {
result = result.queryParam(CATEGORY, this.category);
}
if (!Strings.isNullOrEmpty(this.name)) {
result = result.queryParam(NAME, this.name);
}
if (!Strings.isNullOrEmpty(this.sourceId)) {
result = result.queryParam(SRC_ID, this.sourceId);
}
if (!Strings.isNullOrEmpty(this.destinationId)) {
result = result.queryParam(DEST_ID, this.destinationId);
}
return result;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.FeedCriteria#sourceDatasource(java.lang.String)
*/
@Override
public FeedCriteria sourceDatasource(String dsId) {
this.sourceId = dsId;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.FeedCriteria#destinationDatasource(java.lang.String)
*/
@Override
public FeedCriteria destinationDatasource(String dsId) {
this.destinationId = dsId;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.client.FeedCriteria#name(java.lang.String)
*/
@Override
public FeedCriteria name(String name) {
this.name = name;
return this;
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.rest.model.feed.FeedCriteria#category(java.lang.String)
*/
@Override
public FeedCriteria category(String category) {
this.category = category;
return this;
}
}
private class FeedBuilderImpl implements FeedBuilder {
private String displayName;
private String systemName;
private String systemCategoryName;
private String description;
private String owner;
private List<Metric> preconditionMetrics = new ArrayList<>();
private Properties properties = new Properties();
public FeedBuilderImpl(String category, String name) {
this.systemCategoryName = category;
this.systemName = name;
}
@Override
public FeedBuilder displayName(String name) {
this.displayName = name;
return this;
}
@Override
public FeedBuilder description(String descr) {
this.description = descr;
return this;
}
@Override
public FeedBuilder owner(String owner) {
this.owner = owner;
return this;
}
@Override
public FeedBuilder preconditionMetric(Metric... metrics) {
for (Metric m : metrics) {
this.preconditionMetrics.add(m);
}
return this;
}
@Override
public FeedBuilder property(String key, String value) {
this.properties.setProperty(key, value);
return this;
}
@Override
public Feed build() {
Feed feed = new Feed();
FeedCategory feedCategory = new FeedCategory();
feedCategory.setSystemName(this.systemCategoryName);
feed.setCategory(feedCategory);
feed.setSystemName(this.systemName);
feed.setDisplayName(this.displayName != null ? this.displayName : this.systemName);
feed.setDescription(this.description);
feed.setOwner(this.owner);
feed.setPrecondition(createTrigger(this.preconditionMetrics));
feed.setProperties(properties);
return feed;
}
@Override
public Feed post() {
Feed feed = build();
return postFeed(feed);
}
}
private abstract class DatasourceBuilderImpl<B extends DatasourceBuilder<B, D>, D extends Datasource> implements DatasourceBuilder<B, D> {
protected String name;
protected String description;
protected String owner;
protected boolean encrypted;
protected boolean compressed;
public DatasourceBuilderImpl(String name) {
this.name = name;
}
@Override
public B description(String descr) {
this.description = descr;
return self();
}
@Override
public B owner(String owner) {
this.owner = owner;
return self();
}
@Override
public B encrypted(boolean flag) {
this.encrypted = flag;
return self();
}
@Override
public B compressed(boolean flag) {
this.compressed = flag;
return self();
}
@SuppressWarnings("unchecked")
private B self() {
return (B) this;
}
}
private class HiveTableDatasourceBuilderImpl
extends DatasourceBuilderImpl<HiveTableDatasourceBuilder, HiveTableDatasource>
implements HiveTableDatasourceBuilder {
private String database;
private String tableName;
private String modifiers;
private List<HiveTableColumn> fields = new ArrayList<>();
private List<HiveTablePartition> partitions = new ArrayList<>();
public HiveTableDatasourceBuilderImpl(String name) {
super(name);
}
@Override
public HiveTableDatasourceBuilder database(String name) {
this.database = name;
return this;
}
@Override
public HiveTableDatasourceBuilder tableName(String name) {
this.tableName = name;
return this;
}
@Override
public HiveTableDatasourceBuilder modifiers(String mods) {
this.modifiers = mods;
return this;
}
@Override
public HiveTableDatasourceBuilder field(String name, String type) {
this.fields.add(new HiveTableColumn(name, type));
return this;
}
@Override
public HiveTableDatasourceBuilder partition(String name, String formula, String value, String... more) {
this.partitions.add(new HiveTablePartition(name, formula, value, more));
return this;
}
@Override
public HiveTableDatasource build() {
HiveTableDatasource src = new HiveTableDatasource();
src.setName(this.name);
src.setDescription(this.description);
src.setOwner(this.owner);
src.setEncrypted(this.encrypted);
src.setCompressed(this.compressed);
src.setDatabase(this.database);
src.setTableName(this.tableName);
src.setModifiers(this.modifiers);
src.getColumns().addAll(this.fields);
src.getPartitions().addAll(this.partitions);
return src;
}
@Override
public HiveTableDatasource post() {
HiveTableDatasource ds = build();
return postDatasource(ds);
}
}
private class DirectoryDatasourceBuilderImpl
extends DatasourceBuilderImpl<DirectoryDatasourceBuilder, DirectoryDatasource>
implements DirectoryDatasourceBuilder {
private String path;
private List<String> regexList = new ArrayList<>();
private List<String> globList = new ArrayList<>();
public DirectoryDatasourceBuilderImpl(String name) {
super(name);
}
@Override
public DirectoryDatasourceBuilder path(String path) {
this.path = path;
return this;
}
@Override
public DirectoryDatasourceBuilder regexPattern(String pattern) {
this.regexList.add(pattern);
return this;
}
@Override
public DirectoryDatasourceBuilder globPattern(String pattern) {
this.globList.add(pattern);
return this;
}
@Override
public DirectoryDatasource build() {
DirectoryDatasource src = new DirectoryDatasource();
src.setName(this.name);
src.setDescription(this.description);
src.setOwner(this.owner);
src.setEncrypted(this.encrypted);
src.setCompressed(this.compressed);
src.setPath(this.path);
for (String p : this.regexList) {
src.addRegexPattern(p);
}
for (String p : this.globList) {
src.addGlobPattern(p);
}
return src;
}
@Override
public DirectoryDatasource post() {
DirectoryDatasource dds = build();
return postDatasource(dds);
}
}
}