package com.thinkbiganalytics.metadata.modeshape.feed;
import com.thinkbiganalytics.metadata.api.category.Category;
import com.thinkbiganalytics.metadata.api.datasource.Datasource;
import com.thinkbiganalytics.metadata.api.extension.UserFieldDescriptor;
import com.thinkbiganalytics.metadata.api.feed.Feed;
import com.thinkbiganalytics.metadata.api.feed.FeedDestination;
import com.thinkbiganalytics.metadata.api.feed.FeedPrecondition;
import com.thinkbiganalytics.metadata.api.feed.FeedSource;
import com.thinkbiganalytics.metadata.api.feed.InitializationStatus;
import com.thinkbiganalytics.metadata.api.feed.security.FeedOpsAccessControlProvider;
import com.thinkbiganalytics.metadata.api.security.HadoopSecurityGroup;
import com.thinkbiganalytics.metadata.api.template.FeedManagerTemplate;
import com.thinkbiganalytics.metadata.modeshape.MetadataRepositoryException;
import com.thinkbiganalytics.metadata.modeshape.category.JcrCategory;
import com.thinkbiganalytics.metadata.modeshape.common.AbstractJcrAuditableSystemEntity;
import com.thinkbiganalytics.metadata.modeshape.common.JcrEntity;
import com.thinkbiganalytics.metadata.modeshape.datasource.JcrDatasource;
import com.thinkbiganalytics.metadata.modeshape.feed.security.JcrFeedAllowedActions;
import com.thinkbiganalytics.metadata.modeshape.security.action.JcrAllowedActions;
import com.thinkbiganalytics.metadata.modeshape.security.mixin.AccessControlledMixin;
import com.thinkbiganalytics.metadata.modeshape.sla.JcrServiceLevelAgreement;
import com.thinkbiganalytics.metadata.modeshape.support.JcrPropertyUtil;
import com.thinkbiganalytics.metadata.modeshape.support.JcrUtil;
import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreement;
import org.joda.time.DateTime;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
/*-
* #%L
* thinkbig-metadata-modeshape
* %%
* 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%
*/
/**
* An implementation of {@link Feed} backed by a JCR repository.
*/
public class JcrFeed extends AbstractJcrAuditableSystemEntity implements Feed, AccessControlledMixin {
public static final String PRECONDITION_TYPE = "tba:feedPrecondition";
public static final String NODE_TYPE = "tba:feed";
public static final String SUMMARY = "tba:summary";
public static final String DATA = "tba:data";
private FeedSummary summary;
private FeedData data;
// TODO: Referencing the ops access provider is kind of ugly but is needed so that
// a change to the feed's allowed accessions for ops access get's propagated to the JPA table.
private volatile Optional<FeedOpsAccessControlProvider> opsAccessProvider = Optional.empty();
public JcrFeed(Node node) {
super(node);
}
public JcrFeed(Node node, FeedOpsAccessControlProvider opsAccessProvider) {
super(node);
setOpsAccessProvider(opsAccessProvider);
}
public JcrFeed(Node node, JcrCategory category) {
this(node, (FeedOpsAccessControlProvider) null);
if (category != null) {
getFeedSummary().ifPresent(s -> s.setProperty(FeedSummary.CATEGORY, category));
}
}
public JcrFeed(Node node, JcrCategory category, FeedOpsAccessControlProvider opsAccessProvider) {
this(node, opsAccessProvider);
if (category != null) {
getFeedSummary().ifPresent(s -> s.setProperty(FeedSummary.CATEGORY, category));
}
}
/**
* This should be set after an instance of this type is created to allow the change
* of a feed's operations access control.
*
* @param opsAccessProvider the opsAccessProvider to set
*/
public void setOpsAccessProvider(FeedOpsAccessControlProvider opsAccessProvider) {
this.opsAccessProvider = Optional.ofNullable(opsAccessProvider);
}
public Optional<FeedOpsAccessControlProvider> getOpsAccessProvider() {
return this.opsAccessProvider;
}
// -=-=--=-=- Delegate Propertied methods to data -=-=-=-=-=-
@Override
public Map<String, Object> getProperties() {
return getFeedData().map(d -> d.getProperties()).orElse(Collections.emptyMap());
}
@Override
public void setProperties(Map<String, Object> properties) {
getFeedData().ifPresent(d -> d.setProperties(properties));
}
@Override
public void setProperty(String name, Object value) {
getFeedData().ifPresent(d -> d.setProperty(name, value));
}
@Override
public void removeProperty(String key) {
getFeedData().ifPresent(d -> d.removeProperty(key));
}
@Override
public Map<String, Object> mergeProperties(Map<String, Object> props) {
return getFeedData().map(d -> d.mergeProperties(props)).orElse(Collections.emptyMap());
}
@Override
public Map<String, Object> replaceProperties(Map<String, Object> props) {
return getFeedData().map(d -> d.replaceProperties(props)).orElse(Collections.emptyMap());
}
// -=-=--=-=- Delegate taggable methods to summary -=-=-=-=-=-
@Override
public Set<String> addTag(String tag) {
return getFeedSummary().map(s -> s.addTag(tag)).orElse(Collections.emptySet());
}
@Override
public Set<String> getTags() {
return getFeedSummary().map(s -> s.getTags()).orElse(Collections.emptySet());
}
@Override
public void setTags(Set<String> tags) {
getFeedSummary().ifPresent(s -> s.setTags(tags));
}
@Override
public boolean hasTag(String tag) {
return getFeedSummary().map(s -> s.hasTag(tag)).orElse(false);
}
// -=-=--=-=- Delegate AbstractJcrAuditableSystemEntity methods to summary -=-=-=-=-=-
@Override
public DateTime getCreatedTime() {
return getFeedSummary().map(s -> s.getCreatedTime()).orElse(null);
}
@Override
public void setCreatedTime(DateTime createdTime) {
getFeedSummary().ifPresent(s -> s.setCreatedTime(createdTime));
}
@Override
public DateTime getModifiedTime() {
return getFeedSummary().map(s -> s.getModifiedTime()).orElse(null);
}
@Override
public void setModifiedTime(DateTime modifiedTime) {
getFeedSummary().ifPresent(s -> s.setModifiedTime(modifiedTime));
}
@Override
public String getCreatedBy() {
return getFeedSummary().map(s -> s.getCreatedBy()).orElse(null);
}
@Override
public String getModifiedBy() {
return getFeedSummary().map(s -> s.getModifiedBy()).orElse(null);
}
// -=-=--=-=- Delegate AbstractJcrSystemEntity methods to summary -=-=-=-=-=-
@Override
public String getDescription() {
return getFeedSummary().map(s -> s.getDescription()).orElse(null);
}
@Override
public void setDescription(String description) {
getFeedSummary().ifPresent(s -> s.setDescription(description));
}
@Override
public String getSystemName() {
return getFeedSummary().map(s -> s.getSystemName()).orElse(null);
}
@Override
public void setSystemName(String systemName) {
getFeedSummary().ifPresent(s -> s.setSystemName(systemName));
}
@Override
public String getTitle() {
return getFeedSummary().map(s -> s.getTitle()).orElse(null);
}
@Override
public void setTitle(String title) {
getFeedSummary().ifPresent(s -> s.setTitle(title));
}
public Category getCategory() {
return getFeedSummary().map(s -> s.getCategory(JcrCategory.class)).orElse(null);
}
public FeedManagerTemplate getTemplate() {
return getFeedDetails().map(d -> d.getTemplate()).orElse(null);
}
public void setTemplate(FeedManagerTemplate template) {
getFeedDetails().ifPresent(d -> d.setTemplate(template));
}
public List<? extends FeedSource> getSources() {
return getFeedDetails().map(d -> d.getSources()).orElse(Collections.emptyList());
}
public List<? extends FeedDestination> getDestinations() {
return getFeedDetails().map(d -> d.getDestinations()).orElse(Collections.emptyList());
}
@Override
public String getName() {
return getSystemName();
}
@Override
public String getQualifiedName() {
return getCategory().getName() + "." + getName();
}
@Override
public String getDisplayName() {
return getTitle();
}
@Override
public void setDisplayName(String name) {
setTitle(name);
}
@Override
public Feed.State getState() {
return getFeedData().map(d -> d.getState()).orElse(null);
}
@Override
public void setState(State state) {
getFeedData().ifPresent(d -> d.setState(state));
}
@Override
public boolean isInitialized() {
return getFeedData().map(d -> d.isInitialized()).orElse(null);
}
@Override
public InitializationStatus getCurrentInitStatus() {
return getFeedData().map(d -> d.getCurrentInitStatus()).orElse(null);
}
@Override
public void updateInitStatus(InitializationStatus status) {
getFeedData().ifPresent(d -> d.updateInitStatus(status));
}
@Override
public List<InitializationStatus> getInitHistory() {
return getFeedData().map(d -> d.getInitHistory()).orElse(Collections.emptyList());
}
@Override
public FeedPrecondition getPrecondition() {
return getFeedDetails().map(d -> d.getPrecondition()).orElse(null);
}
public void setPrecondition(JcrServiceLevelAgreement sla) {
// Node precondNode
}
@Override
public Set<String> getWaterMarkNames() {
return getFeedData().map(d -> d.getWaterMarkNames()).orElse(Collections.emptySet());
}
@Override
public Optional<String> getWaterMarkValue(String waterMarkName) {
return getFeedData().flatMap(d -> d.getWaterMarkValue(waterMarkName));
}
@Override
public void setWaterMarkValue(String waterMarkName, String value) {
getFeedData().ifPresent(d -> d.setWaterMarkValue(waterMarkName, value));
}
@Override
public List<Feed> getDependentFeeds() {
return getFeedDetails().map(d -> d.getDependentFeeds()).orElse(Collections.emptyList());
}
@Override
public boolean addDependentFeed(Feed feed) {
return getFeedDetails().map(d -> d.addDependentFeed(feed)).orElse(false);
}
@Override
public boolean removeDependentFeed(Feed feed) {
return getFeedDetails().map(d -> d.removeDependentFeed(feed)).orElse(false);
}
@Override
public List<Feed> getUsedByFeeds() {
return getFeedDetails().map(d -> d.getUsedByFeeds()).orElse(Collections.emptyList());
}
@Override
public boolean addUsedByFeed(Feed feed) {
return getFeedDetails().map(d -> d.addUsedByFeed(feed)).orElse(false);
}
@Override
public boolean removeUsedByFeed(Feed feed) {
return getFeedDetails().map(d -> d.removeUsedByFeed(feed)).orElse(false);
}
@Override
public FeedSource getSource(final Datasource.ID id) {
return getFeedDetails().map(d -> d.getSource(id)).orElse(null);
}
@Override
public FeedDestination getDestination(final Datasource.ID id) {
return getFeedDetails().map(d -> d.getDestination(id)).orElse(null);
}
public String getSchedulePeriod() {
return getFeedData().map(d -> d.getSchedulePeriod()).orElse(null);
}
public void setSchedulePeriod(String schedulePeriod) {
getFeedData().ifPresent(d -> d.setSchedulePeriod(schedulePeriod));
}
public String getScheduleStrategy() {
return getFeedData().map(d -> d.getScheduleStrategy()).orElse(null);
}
public void setScheduleStrategy(String scheduleStrategy) {
getFeedData().ifPresent(d -> d.setScheduleStrategy(scheduleStrategy));
}
public List<ServiceLevelAgreement> getServiceLevelAgreements() {
return getFeedDetails().map(d -> d.getServiceLevelAgreements()).orElse(Collections.emptyList());
}
public void setServiceLevelAgreements(List<? extends ServiceLevelAgreement> serviceLevelAgreements) {
getFeedDetails().ifPresent(d -> d.setServiceLevelAgreements(serviceLevelAgreements));
}
public List<? extends HadoopSecurityGroup> getSecurityGroups() {
return getFeedData().map(d -> d.getSecurityGroups()).orElse(Collections.emptyList());
}
public void setSecurityGroups(List<? extends HadoopSecurityGroup> hadoopSecurityGroups) {
getFeedData().ifPresent(d -> d.setSecurityGroups(hadoopSecurityGroups));
}
public void removeServiceLevelAgreement(ServiceLevelAgreement.ID id) {
getFeedDetails().ifPresent(d -> d.removeServiceLevelAgreement(id));
}
public boolean addServiceLevelAgreement(ServiceLevelAgreement sla) {
return getFeedDetails().map(d -> d.addServiceLevelAgreement(sla)).orElse(false);
}
@Nonnull
@Override
public Map<String, String> getUserProperties() {
return JcrPropertyUtil.getUserProperties(node);
}
@Override
public void setUserProperties(@Nonnull final Map<String, String> userProperties, @Nonnull final Set<UserFieldDescriptor> userFields) {
JcrPropertyUtil.setUserProperties(node, userFields, userProperties);
}
@Override
public String getJson() {
return getFeedDetails().map(d -> d.getJson()).orElse(null);
}
@Override
public void setJson(String json) {
getFeedDetails().ifPresent(d -> d.setJson(json));
}
@Override
public String getNifiProcessGroupId() {
return getFeedDetails().map(d -> d.getNifiProcessGroupId()).orElse(null);
}
@Override
public void setNifiProcessGroupId(String id) {
getFeedDetails().ifPresent(d -> d.setNifiProcessGroupId(id));
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.modeshape.security.mixin.AccessControlledMixin#getJcrAllowedActions()
*/
@Override
public JcrAllowedActions getJcrAllowedActions() {
Node allowedNode = JcrUtil.getNode(getNode(), JcrAllowedActions.NODE_NAME);
return JcrUtil.createJcrObject(allowedNode, getJcrAllowedActionsType(), this.opsAccessProvider.orElse(null));
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.modeshape.security.mixin.AccessControlledMixin#getJcrAllowedActionsType()
*/
@Override
public Class<JcrFeedAllowedActions> getJcrAllowedActionsType() {
return JcrFeedAllowedActions.class;
}
public Optional<FeedSummary> getFeedSummary() {
if (this.summary == null) {
if (JcrUtil.hasNode(getNode(), SUMMARY)) {
this.summary = JcrUtil.getJcrObject(getNode(), SUMMARY, FeedSummary.class, this);
return Optional.of(this.summary);
} else {
return Optional.empty();
}
} else {
return Optional.of(this.summary);
}
}
public Optional<FeedDetails> getFeedDetails() {
Optional<FeedSummary> summary = getFeedSummary();
if (summary.isPresent()) {
return summary.get().getFeedDetails();
} else {
return Optional.empty();
}
}
public Optional<FeedData> getFeedData() {
if (this.data == null) {
if (JcrUtil.hasNode(getNode(), DATA)) {
this.data = JcrUtil.getJcrObject(getNode(), DATA, FeedData.class);
return Optional.of(this.data);
} else {
return Optional.empty();
}
} else {
return Optional.of(this.data);
}
}
@Override
public FeedId getId() {
try {
return new JcrFeed.FeedId(getObjectId());
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to retrieve the entity id", e);
}
}
protected JcrFeedSource ensureFeedSource(JcrDatasource datasource) {
return getFeedDetails().map(d -> d.ensureFeedSource(datasource)).orElse(null);
}
protected JcrFeedDestination ensureFeedDestination(JcrDatasource datasource) {
return getFeedDetails().map(d -> d.ensureFeedDestination(datasource)).orElse(null);
}
protected void removeFeedSource(JcrFeedSource source) {
getFeedDetails().ifPresent(d -> d.removeFeedSource(source));
}
protected void removeFeedDestination(JcrFeedDestination dest) {
getFeedDetails().ifPresent(d -> d.removeFeedDestination(dest));
}
protected void removeFeedSources() {
getFeedDetails().ifPresent(d -> d.removeFeedSources());
}
protected void removeFeedDestinations() {
getFeedDetails().ifPresent(d -> d.removeFeedDestinations());
}
protected Node createNewPrecondition() {
return getFeedDetails().map(d -> d.createNewPrecondition()).orElse(null);
}
public static class FeedId extends JcrEntity.EntityId implements Feed.ID {
public FeedId(Serializable ser) {
super(ser);
}
}
}