Java tutorial
/** * Copyright 2017 Hortonworks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ package com.hortonworks.streamline.streams.catalog.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.io.ByteStreams; import com.hortonworks.registries.common.Schema; import com.hortonworks.streamline.common.ComponentTypes; import com.hortonworks.streamline.common.QueryParam; import com.hortonworks.streamline.common.util.FileStorage; import com.hortonworks.streamline.common.util.ProxyUtil; import com.hortonworks.streamline.common.util.Utils; import com.hortonworks.streamline.common.util.WSUtils; import com.hortonworks.streamline.registries.model.client.MLModelRegistryClient; import com.hortonworks.streamline.storage.PrimaryKey; import com.hortonworks.streamline.storage.Storable; import com.hortonworks.streamline.storage.StorableKey; import com.hortonworks.streamline.storage.StorageManager; import com.hortonworks.streamline.storage.exception.StorageException; import com.hortonworks.streamline.storage.util.StorageUtils; import com.hortonworks.streamline.streams.StreamlineEvent; import com.hortonworks.streamline.streams.catalog.File; import com.hortonworks.streamline.streams.catalog.Notifier; import com.hortonworks.streamline.streams.catalog.Projection; import com.hortonworks.streamline.streams.catalog.Topology; import com.hortonworks.streamline.streams.catalog.TopologyBranchRule; import com.hortonworks.streamline.streams.catalog.TopologyComponent; import com.hortonworks.streamline.streams.catalog.TopologyEdge; import com.hortonworks.streamline.streams.catalog.TopologyEditorMetadata; import com.hortonworks.streamline.streams.catalog.TopologyEditorToolbar; import com.hortonworks.streamline.streams.catalog.TopologyOutputComponent; import com.hortonworks.streamline.streams.catalog.TopologyProcessor; import com.hortonworks.streamline.streams.catalog.TopologyProcessorStreamMapping; import com.hortonworks.streamline.streams.catalog.TopologyRule; import com.hortonworks.streamline.streams.catalog.TopologySink; import com.hortonworks.streamline.streams.catalog.TopologySource; import com.hortonworks.streamline.streams.catalog.TopologySourceStreamMapping; import com.hortonworks.streamline.streams.catalog.TopologyStream; import com.hortonworks.streamline.streams.catalog.TopologyTestRunCase; import com.hortonworks.streamline.streams.catalog.TopologyTestRunCaseSink; import com.hortonworks.streamline.streams.catalog.TopologyTestRunCaseSource; import com.hortonworks.streamline.streams.catalog.TopologyTestRunHistory; import com.hortonworks.streamline.streams.catalog.TopologyVersion; import com.hortonworks.streamline.streams.catalog.TopologyWindow; import com.hortonworks.streamline.streams.catalog.UDF; import com.hortonworks.streamline.streams.catalog.processor.CustomProcessorInfo; import com.hortonworks.streamline.streams.catalog.rule.RuleParser; import com.hortonworks.streamline.streams.catalog.topology.TopologyComponentBundle; import com.hortonworks.streamline.streams.catalog.topology.TopologyComponentUISpecification; import com.hortonworks.streamline.streams.catalog.topology.TopologyData; import com.hortonworks.streamline.streams.catalog.topology.component.TopologyDagBuilder; import com.hortonworks.streamline.streams.catalog.topology.component.TopologyExportVisitor; import com.hortonworks.streamline.streams.catalog.topology.state.TopologyState; import com.hortonworks.streamline.streams.layout.TopologyLayoutConstants; import com.hortonworks.streamline.streams.layout.component.Stream; import com.hortonworks.streamline.streams.layout.component.TopologyDag; import com.hortonworks.streamline.streams.layout.component.impl.NotificationSink; import com.hortonworks.streamline.streams.layout.component.impl.RulesProcessor; import com.hortonworks.streamline.streams.layout.component.rule.Rule; import com.hortonworks.streamline.streams.layout.exception.ComponentConfigException; import com.hortonworks.streamline.streams.layout.storm.FluxComponent; import com.hortonworks.streamline.streams.rule.UDAF; import com.hortonworks.streamline.streams.rule.UDAF2; import com.hortonworks.streamline.streams.rule.UDF2; import com.hortonworks.streamline.streams.rule.UDF3; import com.hortonworks.streamline.streams.rule.UDF4; import com.hortonworks.streamline.streams.rule.UDF5; import com.hortonworks.streamline.streams.rule.UDF6; import com.hortonworks.streamline.streams.rule.UDF7; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static com.hortonworks.streamline.common.ComponentTypes.NOTIFICATION; import static com.hortonworks.streamline.common.util.WSUtils.CURRENT_VERSION; import static com.hortonworks.streamline.common.util.WSUtils.buildEdgesFromQueryParam; import static com.hortonworks.streamline.common.util.WSUtils.buildEdgesToQueryParam; import static com.hortonworks.streamline.common.util.WSUtils.currentVersionQueryParam; import static com.hortonworks.streamline.common.util.WSUtils.versionIdQueryParam; import static com.hortonworks.streamline.streams.catalog.TopologyEdge.StreamGrouping; import static com.hortonworks.streamline.streams.catalog.TopologyEditorMetadata.TopologyUIData; /** * A service layer where we could put our business logic. */ public class StreamCatalogService { private static final Logger LOG = LoggerFactory.getLogger(StreamCatalogService.class); // TODO: the namespace and Id generation logic should be moved inside DAO private static final String NOTIFIER_INFO_NAMESPACE = new Notifier().getNameSpace(); private static final String TOPOLOGY_NAMESPACE = new Topology().getNameSpace(); private static final String TOPOLOGY_VERSIONINFO_NAMESPACE = new TopologyVersion().getNameSpace(); private static final String STREAMINFO_NAMESPACE = new TopologyStream().getNameSpace(); private static final String TOPOLOGY_COMPONENT_NAMESPACE = new TopologyComponent().getNameSpace(); private static final String TOPOLOGY_SOURCE_NAMESPACE = new TopologySource().getNameSpace(); private static final String TOPOLOGY_SOURCE_STREAM_MAPPING_NAMESPACE = new TopologySourceStreamMapping() .getNameSpace(); private static final String TOPOLOGY_SINK_NAMESPACE = new TopologySink().getNameSpace(); private static final String TOPOLOGY_PROCESSOR_NAMESPACE = new TopologyProcessor().getNameSpace(); private static final String TOPOLOGY_PROCESSOR_STREAM_MAPPING_NAMESPACE = new TopologyProcessorStreamMapping() .getNameSpace(); private static final String TOPOLOGY_EDGE_NAMESPACE = new TopologyEdge().getNameSpace(); private static final String TOPOLOGY_RULEINFO_NAMESPACE = new TopologyRule().getNameSpace(); private static final String TOPOLOGY_BRANCHRULEINFO_NAMESPACE = new TopologyBranchRule().getNameSpace(); private static final String TOPOLOGY_WINDOWINFO_NAMESPACE = new TopologyWindow().getNameSpace(); private static final String UDF_NAMESPACE = new UDF().getNameSpace(); private static final String TOPOLOGY_STATE_NAMESPACE = new TopologyState().getNameSpace(); private static final ArrayList<Class<?>> UDF_CLASSES = Lists.newArrayList(UDAF.class, UDAF2.class, com.hortonworks.streamline.streams.rule.UDF.class, UDF2.class, UDF3.class, UDF4.class, UDF5.class, UDF6.class, UDF7.class); public static final long PLACEHOLDER_ID = -1L; private static final String CLONE_SUFFIX = "-clone"; private final StorageManager dao; private final FileStorage fileStorage; private final TopologyDagBuilder topologyDagBuilder; public StreamCatalogService(StorageManager dao, FileStorage fileStorage, MLModelRegistryClient modelRegistryClient) { this.dao = dao; this.fileStorage = fileStorage; this.topologyDagBuilder = new TopologyDagBuilder(this, modelRegistryClient); } public Notifier addNotifierInfo(Notifier notifier) { if (notifier.getId() == null) { notifier.setId(this.dao.nextId(NOTIFIER_INFO_NAMESPACE)); } if (notifier.getTimestamp() == null) { notifier.setTimestamp(System.currentTimeMillis()); } if (StringUtils.isEmpty(notifier.getName())) { throw new StorageException("Notifier name empty"); } this.dao.add(notifier); return notifier; } public Notifier getNotifierInfo(Long id) { Notifier notifier = new Notifier(); notifier.setId(id); return this.dao.get(new StorableKey(NOTIFIER_INFO_NAMESPACE, notifier.getPrimaryKey())); } public Collection<Notifier> listNotifierInfos() { return this.dao.list(NOTIFIER_INFO_NAMESPACE); } public Collection<Notifier> listNotifierInfos(List<QueryParam> params) { return dao.find(NOTIFIER_INFO_NAMESPACE, params); } public Notifier removeNotifierInfo(Long notifierId) { Notifier notifier = new Notifier(); notifier.setId(notifierId); return dao.remove(new StorableKey(NOTIFIER_INFO_NAMESPACE, notifier.getPrimaryKey())); } public Notifier addOrUpdateNotifierInfo(Long id, Notifier notifier) { notifier.setId(id); notifier.setTimestamp(System.currentTimeMillis()); this.dao.addOrUpdate(notifier); return notifier; } public Collection<TopologyVersion> listCurrentTopologyVersionInfos() { return listTopologyVersionInfos(currentVersionQueryParam()); } public Collection<TopologyVersion> listTopologyVersionInfos(List<QueryParam> queryParams) { return dao.find(TOPOLOGY_VERSIONINFO_NAMESPACE, queryParams); } public Optional<TopologyVersion> getCurrentTopologyVersionInfo(Long topologyId) { Collection<TopologyVersion> versions = listTopologyVersionInfos( WSUtils.currentTopologyVersionQueryParam(topologyId, null)); if (versions.isEmpty()) { LOG.warn("No current version for topology " + topologyId); return Optional.empty(); } else if (versions.size() > 1) { throw new IllegalStateException("More than one 'CURRENT' version for topology id: " + topologyId); } return Optional.of(versions.iterator().next()); } // latest version before the CURRENT version public Optional<TopologyVersion> getLatestVersionInfo(Long topologyId) { Collection<TopologyVersion> versions = listTopologyVersionInfos( WSUtils.buildTopologyIdAwareQueryParams(topologyId, null)); return versions.stream().filter(v -> !v.getName().equals(CURRENT_VERSION)) .max((versionInfo1, versionInfo2) -> { // compares the number part from version strings like V1, V2 ... return versionInfo1.getVersionNumber() - versionInfo2.getVersionNumber(); }); } public TopologyVersion getTopologyVersionInfo(Long versionId) { TopologyVersion topologyVersion = new TopologyVersion(); topologyVersion.setId(versionId); return dao.get(topologyVersion.getStorableKey()); } public TopologyVersion addTopologyVersionInfo(TopologyVersion topologyVersion) { if (topologyVersion.getId() == null) { topologyVersion.setId(this.dao.nextId(TOPOLOGY_VERSIONINFO_NAMESPACE)); } if (topologyVersion.getTimestamp() == null) { topologyVersion.setTimestamp(System.currentTimeMillis()); } dao.add(topologyVersion); return topologyVersion; } public TopologyVersion addOrUpdateTopologyVersionInfo(Long versionId, TopologyVersion topologyVersion) { topologyVersion.setId(versionId); topologyVersion.setTimestamp(System.currentTimeMillis()); this.dao.addOrUpdate(topologyVersion); return topologyVersion; } public Long getVersionTimestamp(Long versionId) { TopologyVersion versionInfo = getTopologyVersionInfo(versionId); if (versionInfo == null) { throw new IllegalArgumentException("No version with versionId " + versionId); } return versionInfo.getTimestamp(); } public TopologyVersion updateVersionTimestamp(Long versionId) { return updateVersionTimestamp(versionId, System.currentTimeMillis()); } public TopologyVersion updateVersionTimestamp(Long versionId, Long timestamp) { TopologyVersion topologyVersion = getTopologyVersionInfo(versionId); if (topologyVersion == null) { throw new IllegalStateException("No version with version Id " + versionId); } topologyVersion.setTimestamp(timestamp); dao.addOrUpdate(topologyVersion); return topologyVersion; } public TopologyVersion removeTopologyVersionInfo(Long versionId) { TopologyVersion topologyVersion = new TopologyVersion(); topologyVersion.setId(versionId); return dao.remove(new StorableKey(TOPOLOGY_VERSIONINFO_NAMESPACE, topologyVersion.getPrimaryKey())); } /** * Lists the 'CURRENT' version of topologies */ public Collection<Topology> listTopologies() { List<Topology> topologies = new ArrayList<>(); for (TopologyVersion version : listCurrentTopologyVersionInfos()) { topologies.addAll(listTopologies(version.getId())); } return topologies; } private Collection<Topology> listTopologies(Long versionId) { Collection<Topology> topologies = this.dao.find(TOPOLOGY_NAMESPACE, versionIdQueryParam(versionId)); Long versionTimestamp = getVersionTimestamp(versionId); topologies.forEach(x -> x.setVersionTimestamp(versionTimestamp)); return topologies; } public Collection<Topology> listTopologies(List<QueryParam> queryParams) { Collection<Topology> topologies = this.dao.find(TOPOLOGY_NAMESPACE, queryParams); topologies.forEach(t -> t.setVersionTimestamp(getVersionTimestamp(t.getVersionId()))); return topologies; } public Long getCurrentVersionId(Long topologyId) { Optional<TopologyVersion> versionInfo = getCurrentTopologyVersionInfo(topologyId); return versionInfo.isPresent() ? versionInfo.get().getId() : -1L; } /** * returns the 'CURRENT' version of the topology with given topologyId */ public Topology getTopology(Long topologyId) { return getTopology(topologyId, getCurrentVersionId(topologyId)); } public Topology getTopology(Long topologyId, Long versionId) { Topology topology = new Topology(); topology.setId(topologyId); topology.setVersionId(versionId); Topology result = this.dao.get(topology.getStorableKey()); if (result != null) { result.setVersionTimestamp(getVersionTimestamp(versionId)); } return result; } public Topology addTopology(Topology topology) { validateTopology(topology); boolean storedPlaceholderVersionTopology = false; if (topology.getId() == null) { topology.setId(this.dao.nextId(TOPOLOGY_NAMESPACE)); topology.setVersionId(PLACEHOLDER_ID); this.dao.add(topology); LOG.debug("Added topology {} with placeholder version", topology); storedPlaceholderVersionTopology = true; } long timestamp = System.currentTimeMillis(); topology.setVersionTimestamp(timestamp); TopologyVersion versionInfo = addCurrentTopologyVersionInfo(topology.getId(), timestamp); LOG.debug("Added version info {}", versionInfo); if (storedPlaceholderVersionTopology) { // remove topology with placeholder version first // WARN: don't use removeTopology since it also removes PLACEHOLDER topology version info! removeOnlyTopologyEntity(topology.getId(), topology.getVersionId()); } // put actual version id topology.setVersionId(versionInfo.getId()); this.dao.addOrUpdate(topology); LOG.debug("Added topology {}", topology); return topology; } private Topology removeOnlyTopologyEntity(Long topologyId, Long versionId) { Topology topologyForDelete = new Topology(); topologyForDelete.setId(topologyId); topologyForDelete.setVersionId(versionId); return dao.remove(topologyForDelete.getStorableKey()); } // create a 'CURRENT' version for given topology id private TopologyVersion addCurrentTopologyVersionInfo(Long topologyId, Long timestamp) { TopologyVersion versionInfo = new TopologyVersion(); versionInfo.setName(CURRENT_VERSION); versionInfo.setDescription(""); versionInfo.setTimestamp(timestamp); versionInfo.setTopologyId(topologyId); return addTopologyVersionInfo(versionInfo); } /** * removes the 'CURRENT' version of the topology with the given id. */ public Topology removeTopology(Long topologyId, boolean recurse) { return removeTopology(topologyId, getCurrentVersionId(topologyId), recurse); } public Topology removeTopology(Long topologyId, Long versionId, boolean recurse) { Topology topology = new Topology(); topology.setId(topologyId); topology.setVersionId(versionId); if (recurse) { try { removeTopologyDependencies(topology.getId(), topology.getVersionId()); } catch (Exception ex) { LOG.error("Got exception while removing topology dependencies", ex); throw new RuntimeException(ex); } } Topology removedTopology = dao.remove(topology.getStorableKey()); removeTopologyVersionInfo(versionId); return removedTopology; } private void removeTopologyDependencies(Long topologyId, Long versionId) throws Exception { List<QueryParam> topologyIdVersionIdQueryParams = WSUtils .buildTopologyIdAndVersionIdAwareQueryParams(topologyId, versionId, null); // remove topology test run case Collection<TopologyTestRunCase> runCases = listTopologyTestRunCase(topologyIdVersionIdQueryParams); for (TopologyTestRunCase runCase : runCases) { Collection<TopologyTestRunCaseSource> runCaseSources = listTopologyTestRunCaseSource(topologyId, runCase.getId()); Collection<TopologyTestRunCaseSink> runCaseSinks = listTopologyTestRunCaseSink(topologyId, runCase.getId()); // remove topology test run case source for (TopologyTestRunCaseSource runCaseSource : runCaseSources) { removeTestRunCaseSource(runCaseSource.getId()); } // remove topology test run case sink for (TopologyTestRunCaseSink runCaseSink : runCaseSinks) { removeTestRunCaseSink(runCaseSink.getId()); } removeTestRunCase(topologyId, runCase.getId()); } // remove edges Collection<TopologyEdge> edges = listTopologyEdges(topologyIdVersionIdQueryParams); for (TopologyEdge edge : edges) { removeTopologyEdge(topologyId, edge.getId(), versionId); } // remove rules Collection<TopologyRule> topologyRules = listRules(topologyIdVersionIdQueryParams); for (TopologyRule topologyRule : topologyRules) { removeRule(topologyId, topologyRule.getId(), versionId); } // remove windowed rules Collection<TopologyWindow> topologyWindows = listWindows(topologyIdVersionIdQueryParams); for (TopologyWindow topologyWindow : topologyWindows) { removeWindow(topologyId, topologyWindow.getId(), versionId); } // remove branch rules Collection<TopologyBranchRule> topologyBranchRules = listBranchRules(topologyIdVersionIdQueryParams); for (TopologyBranchRule topologyBranchRule : topologyBranchRules) { removeBranchRule(topologyId, topologyBranchRule.getId(), versionId); } // remove sinks Collection<TopologySink> sinks = listTopologySinks(topologyIdVersionIdQueryParams); for (TopologySink sink : sinks) { removeTopologySink(topologyId, sink.getId(), versionId, false); } // remove processors Collection<TopologyProcessor> processors = listTopologyProcessors(topologyIdVersionIdQueryParams); for (TopologyProcessor processor : processors) { removeTopologyProcessor(topologyId, processor.getId(), versionId, false); } // remove sources Collection<TopologySource> sources = listTopologySources(topologyIdVersionIdQueryParams); for (TopologySource source : sources) { removeTopologySource(topologyId, source.getId(), versionId, false); } // remove output streams Collection<TopologyStream> topologyStreams = listStreamInfos(topologyIdVersionIdQueryParams); for (TopologyStream topologyStream : topologyStreams) { removeStreamInfo(topologyId, topologyStream.getId(), versionId); } // remove topology editor metadata removeTopologyEditorMetadata(topologyId, versionId); } /** * Clones the given version of the topology and all its dependencies to a new 'CURRENT' version. * The ids of the topology and its dependencies are retained. */ public Topology cloneTopologyVersion(Long topologyId, Long versionId) { Topology topology = getTopology(topologyId, versionId); if (topology != null) { try { topology = addTopology(new Topology(topology)); copyTopologyDependencies(topologyId, versionId, topology.getVersionId()); } catch (Exception ex) { LOG.error("Got exception while copying topology dependencies", ex); if (topology != null) { removeTopology(topology.getId(), topology.getVersionId(), true); } throw new RuntimeException(ex); } } return topology; } private void copyTopologyDependencies(Long topologyId, Long oldVersionId, Long newVersionId) throws Exception { List<QueryParam> topologyIdVersionIdQueryParams = WSUtils .buildTopologyIdAndVersionIdAwareQueryParams(topologyId, oldVersionId, null); // topology editor metadata TopologyEditorMetadata metadata = getTopologyEditorMetadata(topologyId, oldVersionId); if (metadata != null) { addTopologyEditorMetadata(topologyId, newVersionId, new TopologyEditorMetadata(metadata)); } // sources, output streams Collection<TopologySource> sources = listTopologySources(topologyIdVersionIdQueryParams); for (TopologySource source : sources) { addTopologySource(topologyId, newVersionId, new TopologySource(source)); } // processors, output streams Collection<TopologyProcessor> processors = listTopologyProcessors(topologyIdVersionIdQueryParams); for (TopologyProcessor processor : processors) { addTopologyProcessor(topologyId, newVersionId, new TopologyProcessor(processor)); } // add sinks Collection<TopologySink> sinks = listTopologySinks(topologyIdVersionIdQueryParams); for (TopologySink sink : sinks) { addTopologySink(topologyId, newVersionId, new TopologySink(sink)); } // branch rules Collection<TopologyBranchRule> topologyBranchRules = listBranchRules(topologyIdVersionIdQueryParams); for (TopologyBranchRule topologyBranchRule : topologyBranchRules) { addBranchRule(topologyId, newVersionId, new TopologyBranchRule(topologyBranchRule)); } // windowed rules Collection<TopologyWindow> topologyWindows = listWindows(topologyIdVersionIdQueryParams); for (TopologyWindow topologyWindow : topologyWindows) { addWindow(topologyId, newVersionId, new TopologyWindow(topologyWindow)); } // rules Collection<TopologyRule> topologyRules = listRules(topologyIdVersionIdQueryParams); for (TopologyRule topologyRule : topologyRules) { addRule(topologyId, newVersionId, new TopologyRule(topologyRule)); } // add edges Collection<TopologyEdge> edges = listTopologyEdges(topologyIdVersionIdQueryParams); for (TopologyEdge edge : edges) { addTopologyEdge(topologyId, newVersionId, new TopologyEdge(edge)); } // add topology test run case Collection<TopologyTestRunCase> runCases = listTopologyTestRunCase(topologyIdVersionIdQueryParams); for (TopologyTestRunCase runCase : runCases) { Collection<TopologyTestRunCaseSource> runCaseSources = listTopologyTestRunCaseSource(topologyId, runCase.getId()); Collection<TopologyTestRunCaseSink> runCaseSinks = listTopologyTestRunCaseSink(topologyId, runCase.getId()); TopologyTestRunCase newCase = addTopologyTestRunCase(topologyId, newVersionId, new TopologyTestRunCase(runCase)); // add topology test run case source for (TopologyTestRunCaseSource runCaseSource : runCaseSources) { addTopologyTestRunCaseSource(newCase.getId(), newVersionId, new TopologyTestRunCaseSource(runCaseSource)); } // add topology test run case sink for (TopologyTestRunCaseSink runCaseSink : runCaseSinks) { addTopologyTestRunCaseSink(newCase.getId(), newVersionId, new TopologyTestRunCaseSink(runCaseSink)); } } } public Topology addOrUpdateTopology(Long topologyId, Topology topology) { return addOrUpdateTopology(topologyId, getCurrentVersionId(topologyId), topology); } private Topology addOrUpdateTopology(Long topologyId, Long versionId, Topology topology) { topology.setId(topologyId); topology.setVersionId(versionId); long timestamp = System.currentTimeMillis(); topology.setVersionTimestamp(timestamp); validateTopology(topology); this.dao.addOrUpdate(topology); updateVersionTimestamp(versionId, timestamp); return topology; } public TopologyComponent getTopologyComponent(Long topologyId, Long topologyComponentId) { TopologyComponent topologyComponent = getTopologySource(topologyId, topologyComponentId); if (topologyComponent == null) { topologyComponent = getTopologyProcessor(topologyId, topologyComponentId); if (topologyComponent == null) { topologyComponent = getTopologySink(topologyId, topologyComponentId); } } return topologyComponent; } public String exportTopology(Topology topology) throws Exception { Preconditions.checkNotNull(topology); TopologyData topologyData = doExportTopology(topology); ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(topologyData); } private TopologyData doExportTopology(Topology topology) throws Exception { TopologyDag dag = topologyDagBuilder.getDag(topology); topology.setTopologyDag(dag); TopologyData topologyData = new TopologyData(); TopologyExportVisitor exportVisitor = new TopologyExportVisitor(topology.getId(), topologyData, this); topologyData.setTopologyName(topology.getName()); topologyData.setConfig(topology.getConfig()); TopologyDag topologyDag = topology.getTopologyDag(); if (topologyDag != null) { topologyDag.traverse(exportVisitor); } topologyData.setMetadata(getTopologyEditorMetadata(topology.getId())); return topologyData; } private List<Long> importOutputStreams(Long newTopologyId, Map<Long, Long> oldToNewStreamIds, List<TopologyStream> streams) { List<Long> importedOutputStreamIds = new ArrayList<>(); for (TopologyStream stream : streams) { Long oldId = stream.getId(); Long newId = oldToNewStreamIds.get(oldId); if (newId == null) { stream.setId(null); TopologyStream addedTopologyStream = addStreamInfo(newTopologyId, stream); newId = addedTopologyStream.getId(); oldToNewStreamIds.put(oldId, newId); } importedOutputStreamIds.add(newId); } return importedOutputStreamIds; } private TopologyComponentBundle getCurrentTopologyComponentBundle( TopologyComponentBundle.TopologyComponentType type, String subType) { Collection<TopologyComponentBundle> bundles = listTopologyComponentBundlesForTypeWithFilter(type, Collections.singletonList(new QueryParam(TopologyComponentBundle.SUB_TYPE, subType))); if (bundles.size() != 1) { throw new IllegalStateException( "Not able to find topology component bundle for type " + type + " sub type " + subType); } return bundles.iterator().next(); } private Topology doImportTopology(Topology newTopology, TopologyData topologyData) throws Exception { List<TopologySource> topologySources = topologyData.getSources(); Map<Long, Long> oldToNewComponentIds = new HashMap<>(); Map<Long, Long> oldToNewRuleIds = new HashMap<>(); Map<Long, Long> oldToNewWindowIds = new HashMap<>(); Map<Long, Long> oldToNewBranchRuleIds = new HashMap<>(); Map<Long, Long> oldToNewStreamIds = new HashMap<>(); // import source streams for (TopologySource topologySource : topologySources) { topologySource.setOutputStreamIds( importOutputStreams(newTopology.getId(), oldToNewStreamIds, topologySource.getOutputStreams())); topologySource.setOutputStreams(null); } // import processor streams for (TopologyProcessor topologyProcessor : topologyData.getProcessors()) { topologyProcessor.setOutputStreamIds(importOutputStreams(newTopology.getId(), oldToNewStreamIds, topologyProcessor.getOutputStreams())); topologyProcessor.setOutputStreams(null); } // import rules for (TopologyRule rule : topologyData.getRules()) { Long currentId = rule.getId(); rule.setId(null); TopologyRule addedRule = addRule(newTopology.getId(), rule); oldToNewRuleIds.put(currentId, addedRule.getId()); } // import windowed rules for (TopologyWindow window : topologyData.getWindows()) { Long currentId = window.getId(); window.setId(null); TopologyWindow addedWindow = addWindow(newTopology.getId(), window); oldToNewWindowIds.put(currentId, addedWindow.getId()); } // import branch rules for (TopologyBranchRule branchRule : topologyData.getBranchRules()) { Long currentId = branchRule.getId(); branchRule.setId(null); TopologyBranchRule addedBranchRule = addBranchRule(newTopology.getId(), branchRule); oldToNewBranchRuleIds.put(currentId, addedBranchRule.getId()); } // import sources for (TopologySource topologySource : topologySources) { Long oldComponentId = topologySource.getId(); topologySource.setId(null); topologySource.setTopologyId(newTopology.getId()); TopologyComponentBundle bundle = getCurrentTopologyComponentBundle( TopologyComponentBundle.TopologyComponentType.SOURCE, topologyData.getBundleIdToType().get(topologySource.getTopologyComponentBundleId().toString())); topologySource.setTopologyComponentBundleId(bundle.getId()); addTopologySource(newTopology.getId(), topologySource); oldToNewComponentIds.put(oldComponentId, topologySource.getId()); } // import processors for (TopologyProcessor topologyProcessor : topologyData.getProcessors()) { Long oldComponentId = topologyProcessor.getId(); topologyProcessor.setId(null); topologyProcessor.setTopologyId(newTopology.getId()); TopologyComponentBundle bundle; String subType = topologyData.getBundleIdToType() .get(topologyProcessor.getTopologyComponentBundleId().toString()); if (TopologyLayoutConstants.JSON_KEY_CUSTOM_PROCESSOR_SUB_TYPE.equals(subType)) { QueryParam queryParam = new QueryParam(CustomProcessorInfo.NAME, topologyProcessor.getConfig().get(CustomProcessorInfo.NAME)); Collection<TopologyComponentBundle> result = listCustomProcessorBundlesWithFilter( Collections.singletonList(queryParam)); if (result == null || result.size() != 1) { throw new IllegalStateException( "Not able to find topology component bundle for custom processor :" + topologyProcessor.getConfig().get(CustomProcessorInfo.NAME)); } bundle = result.iterator().next(); } else { bundle = getCurrentTopologyComponentBundle(TopologyComponentBundle.TopologyComponentType.PROCESSOR, subType); } topologyProcessor.setTopologyComponentBundleId(bundle.getId()); Optional<Object> ruleListObj = topologyProcessor.getConfig() .getAnyOptional(RulesProcessor.CONFIG_KEY_RULES); ruleListObj.ifPresent(ruleList -> { List<Long> ruleIds = new ObjectMapper().convertValue(ruleList, new TypeReference<List<Long>>() { }); List<Long> updatedRuleIds = new ArrayList<>(); if (ComponentTypes.RULE.equals(bundle.getSubType()) || ComponentTypes.PROJECTION.equals(bundle.getSubType())) { ruleIds.forEach(ruleId -> updatedRuleIds.add(oldToNewRuleIds.get(ruleId))); } else if (bundle.getSubType().equals(ComponentTypes.BRANCH)) { ruleIds.forEach(ruleId -> updatedRuleIds.add(oldToNewBranchRuleIds.get(ruleId))); } else if (bundle.getSubType().equals(ComponentTypes.WINDOW)) { ruleIds.forEach(ruleId -> updatedRuleIds.add(oldToNewWindowIds.get(ruleId))); } topologyProcessor.getConfig().setAny(RulesProcessor.CONFIG_KEY_RULES, updatedRuleIds); }); addTopologyProcessor(newTopology.getId(), topologyProcessor); oldToNewComponentIds.put(oldComponentId, topologyProcessor.getId()); } // import sinks for (TopologySink topologySink : topologyData.getSinks()) { topologySink.setTopologyId(newTopology.getId()); Long currentId = topologySink.getId(); topologySink.setId(null); TopologyComponentBundle bundle = getCurrentTopologyComponentBundle( TopologyComponentBundle.TopologyComponentType.SINK, topologyData.getBundleIdToType().get(topologySink.getTopologyComponentBundleId().toString())); topologySink.setTopologyComponentBundleId(bundle.getId()); if (bundle.getSubType().equals(NOTIFICATION)) { updateNotifierJarFileName(topologySink); } addTopologySink(newTopology.getId(), topologySink); oldToNewComponentIds.put(currentId, topologySink.getId()); } // import edges for (TopologyEdge topologyEdge : topologyData.getEdges()) { List<StreamGrouping> streamGroupings = topologyEdge.getStreamGroupings(); for (StreamGrouping streamGrouping : streamGroupings) { Long newStreamId = oldToNewStreamIds.get(streamGrouping.getStreamId()); streamGrouping.setStreamId(newStreamId); } topologyEdge.setId(null); topologyEdge.setTopologyId(newTopology.getId()); topologyEdge.setFromId(oldToNewComponentIds.get(topologyEdge.getFromId())); topologyEdge.setToId(oldToNewComponentIds.get(topologyEdge.getToId())); addTopologyEdge(newTopology.getId(), topologyEdge); } // import topology editor metadata TopologyEditorMetadata topologyEditorMetadata = topologyData.getTopologyEditorMetadata(); topologyEditorMetadata.setTopologyId(newTopology.getId()); if (topologyEditorMetadata.getData() != null) { TopologyUIData topologyUIData = new ObjectMapper().readValue(topologyEditorMetadata.getData(), TopologyUIData.class); topologyUIData.getSources().forEach(c -> c.setId(oldToNewComponentIds.get(c.getId()))); topologyUIData.getProcessors().forEach(c -> c.setId(oldToNewComponentIds.get(c.getId()))); topologyUIData.getSinks().forEach(c -> c.setId(oldToNewComponentIds.get(c.getId()))); topologyEditorMetadata.setData(new ObjectMapper().writeValueAsString(topologyUIData)); } else { topologyEditorMetadata.setData(StringUtils.EMPTY); } addTopologyEditorMetadata(newTopology.getId(), topologyData.getTopologyEditorMetadata()); return newTopology; } private void updateNotifierJarFileName(TopologySink sink) { String notifierClassName = sink.getConfig().getString(Notifier.CLASS_NAME, ""); if (!notifierClassName.isEmpty()) { Collection<Notifier> notifiers = listNotifierInfos( QueryParam.params(Notifier.CLASS_NAME, notifierClassName)); if (notifiers.isEmpty()) { throw new IllegalStateException( "No registered notifier in the cluster for class '" + notifierClassName + "'"); } Notifier current = notifiers.iterator().next(); sink.getConfig().setAny(Notifier.JARFILE_NAME, current.getJarFileName()); } } public Topology importTopology(Long namespaceId, TopologyData topologyData) throws Exception { Preconditions.checkNotNull(topologyData); Topology newTopology = new Topology(); try { newTopology.setName(topologyData.getTopologyName()); newTopology.setConfig(topologyData.getConfig()); newTopology.setNamespaceId(namespaceId); addTopology(newTopology); } catch (Exception ex) { LOG.error("Got exception while importing the topology", ex); throw ex; } try { doImportTopology(newTopology, topologyData); } catch (Exception ex) { LOG.error("Got exception while importing the topology", ex); removeTopology(newTopology.getId(), true); throw ex; } return newTopology; } public Topology cloneTopology(Long namespaceId, Topology topology) throws Exception { Preconditions.checkNotNull(topology, "Topology does not exist"); TopologyData exported = new TopologyData(doExportTopology(topology)); Optional<String> latest = getLatestCloneName(exported.getTopologyName(), listTopologies()); exported.setTopologyName(getNextCloneName(latest.orElse(topology.getName()))); if (namespaceId == null) { namespaceId = topology.getNamespaceId(); } return importTopology(namespaceId, exported); } Optional<String> getLatestCloneName(String topologyName, Collection<Topology> topologies) { return Utils.getLatestName(topologies.stream().map(Topology::getName).collect(Collectors.toSet()), Utils.getPrefix(topologyName, CLONE_SUFFIX), CLONE_SUFFIX); } String getNextCloneName(String topologyName) { return Utils.getNextName(topologyName, CLONE_SUFFIX); } public Optional<TopologyState> getTopologyState(Long topologyId) { TopologyState state = new TopologyState(); state.setTopologyId(topologyId); TopologyState result = this.dao.get(state.getStorableKey()); return Optional.ofNullable(result); } public TopologyState addTopologyState(Long topologyId, TopologyState state) { state.setTopologyId(topologyId); this.dao.add(state); return state; } public TopologyState addOrUpdateTopologyState(Long topologyID, TopologyState state) { state.setTopologyId(topologyID); dao.addOrUpdate(state); return state; } public TopologyState removeTopologyState(Long topologyId) { TopologyState state = new TopologyState(); state.setTopologyId(topologyId); return dao.remove(new StorableKey(TOPOLOGY_STATE_NAMESPACE, state.getPrimaryKey())); } public Collection<TopologyComponentBundle.TopologyComponentType> listTopologyComponentBundleTypes() { return Arrays.asList(TopologyComponentBundle.TopologyComponentType.values()); } public Collection<TopologyComponentBundle> listTopologyComponentBundlesForTypeWithFilter( TopologyComponentBundle.TopologyComponentType componentType, List<QueryParam> params) { List<TopologyComponentBundle> topologyComponentBundles = new ArrayList<>(); String ns = TopologyComponentBundle.NAME_SPACE; Collection<TopologyComponentBundle> filtered = dao.find(ns, params); for (TopologyComponentBundle tcb : filtered) { if (tcb.getType().equals(componentType)) { topologyComponentBundles.add(tcb); } } return topologyComponentBundles; } public TopologyComponentBundle getTopologyComponentBundle(Long topologyComponentBundleId) { TopologyComponentBundle topologyComponentBundle = new TopologyComponentBundle(); topologyComponentBundle.setId(topologyComponentBundleId); return this.dao.get(topologyComponentBundle.getStorableKey()); } public TopologyComponentBundle addTopologyComponentBundle(TopologyComponentBundle topologyComponentBundle, InputStream bundleJar) throws ComponentConfigException, IOException { topologyComponentBundle.getTopologyComponentUISpecification().validate(); loadTransformationClassForBundle(topologyComponentBundle, bundleJar); if (!topologyComponentBundle.getBuiltin()) { topologyComponentBundle.setBundleJar(getTopologyComponentBundleJarName(topologyComponentBundle)); uploadFileToStorage(bundleJar, topologyComponentBundle.getBundleJar()); } try { if (topologyComponentBundle.getId() == null) { topologyComponentBundle.setId(this.dao.nextId(TopologyComponentBundle.NAME_SPACE)); } if (topologyComponentBundle.getTimestamp() == null) { topologyComponentBundle.setTimestamp(System.currentTimeMillis()); } this.dao.add(topologyComponentBundle); } catch (StorageException e) { if (!topologyComponentBundle.getBuiltin()) { LOG.debug("StorageException while adding the bundle. Deleting the bundle jar."); deleteFileFromStorage(topologyComponentBundle.getBundleJar()); } throw e; } return topologyComponentBundle; } public TopologyComponentBundle addOrUpdateTopologyComponentBundle(Long id, TopologyComponentBundle topologyComponentBundle, InputStream bundleJar) throws ComponentConfigException, IOException { topologyComponentBundle.getTopologyComponentUISpecification().validate(); loadTransformationClassForBundle(topologyComponentBundle, bundleJar); if (!topologyComponentBundle.getBuiltin()) { topologyComponentBundle.setBundleJar(getTopologyComponentBundleJarName(topologyComponentBundle)); uploadFileToStorage(bundleJar, topologyComponentBundle.getBundleJar()); } TopologyComponentBundle existing = new TopologyComponentBundle(); existing.setId(id); existing = this.dao.get(existing.getStorableKey()); if (!existing.getBuiltin()) { try { deleteFileFromStorage(existing.getBundleJar()); } catch (IOException e) { if (!topologyComponentBundle.getBuiltin()) { deleteFileFromStorage(topologyComponentBundle.getBundleJar()); } throw e; } } try { topologyComponentBundle.setId(id); topologyComponentBundle.setTimestamp(System.currentTimeMillis()); this.dao.addOrUpdate(topologyComponentBundle); } catch (StorageException e) { if (!topologyComponentBundle.getBuiltin()) { deleteFileFromStorage(topologyComponentBundle.getBundleJar()); } throw e; } return topologyComponentBundle; } public TopologyComponentBundle removeTopologyComponentBundle(Long id) throws IOException { TopologyComponentBundle topologyComponentBundle = new TopologyComponentBundle(); topologyComponentBundle.setId(id); TopologyComponentBundle existing = this.dao.get(topologyComponentBundle.getStorableKey()); if (!existing.getBuiltin()) { deleteFileFromStorage(existing.getBundleJar()); } return dao.remove(existing.getStorableKey()); } public InputStream getFileFromJarStorage(String fileName) throws IOException { return this.fileStorage.downloadFile(fileName); } public Collection<CustomProcessorInfo> listCustomProcessorsFromBundleWithFilter(List<QueryParam> params) throws IOException { Collection<TopologyComponentBundle> customProcessors = this.listCustomProcessorBundlesWithFilter(params); Collection<CustomProcessorInfo> result = new ArrayList<>(); for (TopologyComponentBundle cp : customProcessors) { CustomProcessorInfo customProcessorInfo = new CustomProcessorInfo(); result.add(customProcessorInfo.fromTopologyComponentBundle(cp)); } return result; } private Collection<TopologyComponentBundle> listCustomProcessorBundlesWithFilter(List<QueryParam> params) throws IOException { List<QueryParam> queryParamsForTopologyComponent = new ArrayList<>(); queryParamsForTopologyComponent.add(new QueryParam(TopologyComponentBundle.SUB_TYPE, TopologyLayoutConstants.JSON_KEY_CUSTOM_PROCESSOR_SUB_TYPE)); for (QueryParam qp : params) { if (qp.getName().equals(TopologyComponentBundle.STREAMING_ENGINE)) { queryParamsForTopologyComponent.add(qp); } } Collection<TopologyComponentBundle> customProcessors = this.listTopologyComponentBundlesForTypeWithFilter( TopologyComponentBundle.TopologyComponentType.PROCESSOR, queryParamsForTopologyComponent); Collection<TopologyComponentBundle> result = new ArrayList<>(); for (TopologyComponentBundle cp : customProcessors) { Map<String, Object> config = new HashMap<>(); for (TopologyComponentUISpecification.UIField uiField : cp.getTopologyComponentUISpecification() .getFields()) { config.put(uiField.getFieldName(), uiField.getDefaultValue()); } boolean matches = true; for (QueryParam qp : params) { if (!qp.getName().equals(TopologyComponentBundle.STREAMING_ENGINE) && !qp.getValue().equals(config.get(qp.getName()))) { matches = false; break; } } if (matches) { result.add(cp); } } return result; } public CustomProcessorInfo addCustomProcessorInfoAsBundle(CustomProcessorInfo customProcessorInfo, InputStream jarFile) throws IOException, ComponentConfigException { try { uploadFileToStorage(jarFile, customProcessorInfo.getJarFileName()); TopologyComponentBundle topologyComponentBundle = customProcessorInfo.toTopologyComponentBundle(); this.addTopologyComponentBundle(topologyComponentBundle, null); } catch (IOException e) { LOG.error("IOException thrown while trying to upload jar for %s", customProcessorInfo.getName()); throw e; } catch (StorageException se) { LOG.error("StorageException thrown while adding custom processor info %s", customProcessorInfo.getName()); try { deleteFileFromStorage(customProcessorInfo.getJarFileName()); } catch (IOException e) { LOG.error("Unexpected exception thrown while cleaning up custom processor info %s", customProcessorInfo.getName()); throw e; } throw se; } return customProcessorInfo; } public CustomProcessorInfo updateCustomProcessorInfoAsBundle(CustomProcessorInfo customProcessorInfo, InputStream jarFile) throws IOException, ComponentConfigException { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam(CustomProcessorInfo.NAME, customProcessorInfo.getName())); Collection<TopologyComponentBundle> result = this.listCustomProcessorBundlesWithFilter(queryParams); if (result.isEmpty() || result.size() != 1) { throw new IOException("Failed to update custom processor with name:" + customProcessorInfo.getName()); } TopologyComponentBundle customProcessorBundle = result.iterator().next(); deleteFileFromStorage( new CustomProcessorInfo().fromTopologyComponentBundle(customProcessorBundle).getJarFileName()); uploadFileToStorage(jarFile, customProcessorInfo.getJarFileName()); TopologyComponentBundle newCustomProcessorBundle = customProcessorInfo.toTopologyComponentBundle(); this.addOrUpdateTopologyComponentBundle(customProcessorBundle.getId(), newCustomProcessorBundle, null); return customProcessorInfo; } public String uploadFileToStorage(InputStream inputStream, String jarFileName) throws IOException { return fileStorage.uploadFile(inputStream, jarFileName); } public InputStream downloadFileFromStorage(String jarName) throws IOException { return fileStorage.downloadFile(jarName); } public boolean deleteFileFromStorage(String jarName) throws IOException { return fileStorage.deleteFile(jarName); } public CustomProcessorInfo removeCustomProcessorInfoAsBundle(String name) throws IOException { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam(CustomProcessorInfo.NAME, name)); Collection<TopologyComponentBundle> result = this.listCustomProcessorBundlesWithFilter(queryParams); if (result.isEmpty() || result.size() != 1) { throw new IOException("Failed to delete custom processor with name:" + name); } TopologyComponentBundle customProcessorBundle = result.iterator().next(); Collection<TopologyProcessor> processors = this.listTopologyProcessors(); if (processors != null && !processors.isEmpty()) { for (TopologyProcessor topologyProcessor : processors) { if (topologyProcessor.getTopologyComponentBundleId().equals(customProcessorBundle.getId())) { throw new IOException( "Cannot delete custom processor as it is being used in one of the topologies."); } } } CustomProcessorInfo customProcessorInfo = new CustomProcessorInfo(); deleteFileFromStorage( customProcessorInfo.fromTopologyComponentBundle(customProcessorBundle).getJarFileName()); this.removeTopologyComponentBundle(customProcessorBundle.getId()); return customProcessorInfo; } public Optional<TopologyEditorToolbar> getTopologyEditorToolbar(long userId) { List<QueryParam> qps = QueryParam.params(TopologyEditorToolbar.USER_ID, String.valueOf(userId)); Collection<TopologyEditorToolbar> res = listTopologyEditorToolbar(qps); return res.isEmpty() ? Optional.empty() : Optional.ofNullable(res.iterator().next()); } public Collection<TopologyEditorToolbar> listTopologyEditorToolbar(List<QueryParam> queryParams) { return this.dao.find(TopologyEditorToolbar.NAMESPACE, queryParams); } public TopologyEditorToolbar addTopologyEditorToolbar(TopologyEditorToolbar toolbar) { if (toolbar.getUserId() == null) { toolbar.setUserId(-1L); } if (toolbar.getTimestamp() == null) { toolbar.setTimestamp(System.currentTimeMillis()); } this.dao.add(toolbar); return toolbar; } public TopologyEditorToolbar addOrUpdateTopologyEditorToolbar(TopologyEditorToolbar toolbar) { if (toolbar.getUserId() == null) { toolbar.setUserId(-1L); } toolbar.setTimestamp(System.currentTimeMillis()); this.dao.addOrUpdate(toolbar); return toolbar; } public Optional<TopologyEditorToolbar> removeTopologyEditorToolbar(long userId) { return getTopologyEditorToolbar(userId) .map(toolbar -> dao.<TopologyEditorToolbar>remove(toolbar.getStorableKey())); } public Collection<TopologyEditorMetadata> listTopologyEditorMetadata() { List<TopologyEditorMetadata> metadatas = new ArrayList<>(); Collection<TopologyVersion> currentVersions = listCurrentTopologyVersionInfos(); for (TopologyVersion version : currentVersions) { List<QueryParam> queryParams = WSUtils .buildTopologyIdAndVersionIdAwareQueryParams(version.getTopologyId(), version.getId(), null); metadatas.addAll(listTopologyEditorMetadata(queryParams)); } return metadatas; } public Collection<TopologyEditorMetadata> listTopologyEditorMetadata(List<QueryParam> queryParams) { return this.dao.find(TopologyEditorMetadata.NAME_SPACE, queryParams); } public TopologyEditorMetadata getTopologyEditorMetadata(Long topologyId) { return getTopologyEditorMetadata(topologyId, getCurrentVersionId(topologyId)); } public TopologyEditorMetadata getTopologyEditorMetadata(Long topologyId, Long versionId) { TopologyEditorMetadata topologyEditorMetadata = new TopologyEditorMetadata(); topologyEditorMetadata.setTopologyId(topologyId); topologyEditorMetadata.setVersionId(versionId); return this.dao.get(topologyEditorMetadata.getStorableKey()); } public TopologyEditorMetadata addTopologyEditorMetadata(Long topologyId, TopologyEditorMetadata topologyEditorMetadata) { return addTopologyEditorMetadata(topologyId, getCurrentVersionId(topologyId), topologyEditorMetadata); } public TopologyEditorMetadata addTopologyEditorMetadata(Long topologyId, Long versionId, TopologyEditorMetadata topologyEditorMetadata) { long timestamp = System.currentTimeMillis(); topologyEditorMetadata.setTimestamp(timestamp); topologyEditorMetadata.setVersionId(versionId); this.dao.add(topologyEditorMetadata); updateVersionTimestamp(versionId, timestamp); return topologyEditorMetadata; } public TopologyEditorMetadata addOrUpdateTopologyEditorMetadata(Long topologyId, TopologyEditorMetadata topologyEditorMetadata) { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologyEditorMetadata.setTopologyId(topologyId); topologyEditorMetadata.setVersionId(currentTopologyVersionId); long timestamp = System.currentTimeMillis(); topologyEditorMetadata.setTimestamp(timestamp); this.dao.addOrUpdate(topologyEditorMetadata); updateVersionTimestamp(currentTopologyVersionId, timestamp); return topologyEditorMetadata; } public TopologyEditorMetadata removeTopologyEditorMetadata(Long topologyId) { return removeTopologyEditorMetadata(topologyId, getCurrentVersionId(topologyId)); } public TopologyEditorMetadata removeTopologyEditorMetadata(Long topologyId, Long versionId) { TopologyEditorMetadata topologyEditorMetadata = getTopologyEditorMetadata(topologyId, versionId); if (topologyEditorMetadata != null) { topologyEditorMetadata = dao.remove(topologyEditorMetadata.getStorableKey()); topologyEditorMetadata.setTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologyEditorMetadata; } /** * returns the 'CURRENT' version of the source with given source Id */ public TopologySource getTopologySource(Long topologyId, Long sourceId) { return getTopologySource(topologyId, sourceId, getCurrentVersionId(topologyId)); } public TopologySource getTopologySource(Long topologyId, Long sourceId, Long versionId) { TopologySource topologySource = new TopologySource(); topologySource.setId(sourceId); topologySource.setVersionId(versionId); TopologySource source = dao.get(new StorableKey(TOPOLOGY_SOURCE_NAMESPACE, topologySource.getPrimaryKey())); if (source == null || !source.getTopologyId().equals(topologyId)) { return null; } fillSourceStreams(source); return source; } /** * Generate id from the {@link TopologyComponent} namespace * so that its unique across source, sink and processors. * Similar to Table per concrete class hibernate strategy. */ private Long getNextTopologyComponentId() { TopologyComponent component = new TopologyComponent(); Long id = dao.nextId(TOPOLOGY_COMPONENT_NAMESPACE); component.setId(id); dao.add(component); dao.remove(component.getStorableKey()); return component.getId(); } /* * Handle this check at application layer since in-memory storage * does not contain unique key constraint. * * Other checks can be added later. */ private void validateTopology(Topology topology) { StorageUtils.ensureUnique(topology, this::listTopologies, QueryParam.params(Topology.NAME, topology.getName())); } private void validateTopologySource(TopologySource topologySource) { StorageUtils.ensureUnique(topologySource, this::listTopologySources, QueryParam.params(TopologySource.TOPOLOGYID, topologySource.getTopologyId().toString(), TopologySource.VERSIONID, topologySource.getVersionId().toString(), TopologySource.NAME, topologySource.getName())); } private void validateTopologySink(TopologySink topologySink) { StorageUtils.ensureUnique(topologySink, this::listTopologySinks, QueryParam.params(TopologySink.TOPOLOGYID, topologySink.getTopologyId().toString(), TopologySink.VERSIONID, topologySink.getVersionId().toString(), TopologySink.NAME, topologySink.getName())); } private void validateTopologyProcessor(TopologyProcessor topologyProcessor) { StorageUtils.ensureUnique(topologyProcessor, this::listTopologyProcessors, QueryParam.params(TopologyProcessor.TOPOLOGYID, topologyProcessor.getTopologyId().toString(), TopologyProcessor.VERSIONID, topologyProcessor.getVersionId().toString(), TopologyProcessor.NAME, topologyProcessor.getName())); } public TopologySource addTopologySource(Long topologyId, TopologySource topologySource) { return addTopologySource(topologyId, getCurrentVersionId(topologyId), topologySource); } public TopologySource addTopologySource(Long topologyId, Long versionId, TopologySource topologySource) { if (topologySource.getId() == null) { topologySource.setId(getNextTopologyComponentId()); } topologySource.setVersionId(versionId); topologySource.setTopologyId(topologyId); validateTopologySource(topologySource); List<TopologyStream> topologyStreams = addTopologyOutputComponent(topologySource); addSourceStreamMapping(topologySource, topologySource.getOutputStreamIds()); topologySource.setOutputStreams(topologyStreams); topologySource.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); return topologySource; } private List<TopologyStream> addTopologyOutputComponent(TopologyOutputComponent outputComponent) { List<TopologyStream> topologyStreams; if (outputComponent.getOutputStreams() != null) { topologyStreams = addOutputStreams(outputComponent.getTopologyId(), outputComponent.getVersionId(), outputComponent.getOutputStreams()); outputComponent.setOutputStreamIds( new ArrayList<>(Collections2.transform(topologyStreams, new Function<TopologyStream, Long>() { @Override public Long apply(TopologyStream input) { return input.getId(); } }))); } else if (outputComponent.getOutputStreamIds() != null) { topologyStreams = getOutputStreams(outputComponent.getTopologyId(), outputComponent.getVersionId(), outputComponent.getOutputStreamIds()); } else { topologyStreams = Collections.emptyList(); outputComponent.setOutputStreamIds(Collections.<Long>emptyList()); } dao.add(outputComponent); return topologyStreams; } private List<TopologyStream> getOutputStreams(Long topologyId, Long versionId, List<Long> outputStreamIds) { List<TopologyStream> topologyStreams = new ArrayList<>(); for (Long outputStreamId : outputStreamIds) { TopologyStream topologyStream; if ((topologyStream = getStreamInfo(topologyId, outputStreamId, versionId)) == null) { throw new IllegalArgumentException( "Output stream with id '" + outputStreamId + "' does not exist."); } topologyStreams.add(topologyStream); } return topologyStreams; } public TopologySource addOrUpdateTopologySource(Long topologyId, Long sourceId, TopologySource topologySource) { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologySource.setId(sourceId); topologySource.setVersionId(currentTopologyVersionId); topologySource.setTopologyId(topologyId); validateTopologySource(topologySource); dao.addOrUpdate(topologySource); List<Long> newList = Collections.emptyList(); if (topologySource.getOutputStreamIds() != null) { newList = topologySource.getOutputStreamIds(); } else if (topologySource.getOutputStreams() != null) { newList = updateOutputStreams(topologySource); } List<Long> existing = getOutputStreamIds(topologySource); Sets.SetView<Long> streamIdsToRemove = Sets.difference(ImmutableSet.copyOf(existing), ImmutableSet.copyOf(newList)); Sets.SetView<Long> streamIdsToAdd = Sets.difference(ImmutableSet.copyOf(newList), ImmutableSet.copyOf(existing)); removeSourceStreamMapping(topologySource, Lists.newArrayList(streamIdsToRemove)); addSourceStreamMapping(topologySource, Lists.newArrayList(streamIdsToAdd)); TopologySource updatedSource = getTopologySource(topologyId, sourceId, currentTopologyVersionId); updatedSource.setVersionTimestamp(updateVersionTimestamp(currentTopologyVersionId).getTimestamp()); return updatedSource; } private List<Long> updateOutputStreams(TopologyOutputComponent outputComponent) { List<Long> newStreamIds = new ArrayList<>(); for (TopologyStream topologyStream : outputComponent.getOutputStreams()) { if (topologyStream.getId() != null && getStreamInfo(outputComponent.getTopologyId(), topologyStream.getId()) != null) { addOrUpdateStreamInfo(outputComponent.getTopologyId(), topologyStream.getId(), topologyStream); newStreamIds.add(topologyStream.getId()); } else { newStreamIds.add(addStreamInfo(outputComponent.getTopologyId(), topologyStream).getId()); } } return newStreamIds; } public TopologySource removeTopologySource(Long topologyId, Long sourceId, boolean removeEdges) { return removeTopologySource(topologyId, sourceId, getCurrentVersionId(topologyId), removeEdges); } public TopologySource removeTopologySource(Long topologyId, Long sourceId, Long versionId, boolean removeEdges) { TopologySource topologySource = getTopologySource(topologyId, sourceId, versionId); if (topologySource != null) { if (removeEdges) { removeAllEdges(topologySource); } removeSourceStreamMapping(topologySource); topologySource = dao.<TopologySource>remove( new StorableKey(TOPOLOGY_SOURCE_NAMESPACE, topologySource.getPrimaryKey())); topologySource.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologySource; } // removes any incoming or outgoing edges from the component private void removeAllEdges(TopologyComponent c) { removeTopologyEdge(buildEdgesFromQueryParam(c.getTopologyId(), c.getVersionId(), c.getId())); removeTopologyEdge(buildEdgesToQueryParam(c.getTopologyId(), c.getVersionId(), c.getId())); } private void removeTopologyEdge(List<QueryParam> queryParams) { try { listTopologyEdges(queryParams).forEach(edge -> { LOG.debug("Removing edge {}", edge); removeTopologyEdge(edge.getTopologyId(), edge.getId(), edge.getVersionId()); }); } catch (Exception ex) { LOG.error("Got exception while removing edge", ex); throw new RuntimeException(ex); } } public Collection<TopologySource> listTopologySources() { return fillSourceStreams(dao.<TopologySource>list(TOPOLOGY_SOURCE_NAMESPACE)); } public Collection<TopologySource> listTopologySources(List<QueryParam> params) { return fillSourceStreams(dao.<TopologySource>find(TOPOLOGY_SOURCE_NAMESPACE, params)); } private List<TopologyStream> addOutputStreams(Long topologyId, Long versionId, List<TopologyStream> streams) { List<TopologyStream> topologyStreams = new ArrayList<>(); for (TopologyStream outputStream : streams) { topologyStreams.add(addStreamInfo(topologyId, versionId, outputStream)); } return topologyStreams; } private void addSourceStreamMapping(TopologySource topologySource, List<Long> streamIds) { for (Long outputStreamId : streamIds) { dao.<TopologySourceStreamMapping>add(new TopologySourceStreamMapping(topologySource.getId(), topologySource.getVersionId(), outputStreamId)); } } private void removeSourceStreamMapping(TopologySource topologySource) { if (topologySource != null) { removeSourceStreamMapping(topologySource, topologySource.getOutputStreamIds()); } } private void removeSourceStreamMapping(TopologySource topologySource, List<Long> streamIds) { if (topologySource != null) { for (Long outputStreamId : streamIds) { TopologySourceStreamMapping mapping = new TopologySourceStreamMapping(topologySource.getId(), topologySource.getVersionId(), outputStreamId); dao.<TopologySourceStreamMapping>remove(mapping.getStorableKey()); } } } private List<Long> getOutputStreamIds(TopologySource topologySource) { List<Long> streamIds = new ArrayList<>(); if (topologySource != null) { QueryParam qp1 = new QueryParam(TopologySourceStreamMapping.FIELD_SOURCE_ID, String.valueOf(topologySource.getId())); QueryParam qp2 = new QueryParam(TopologySourceStreamMapping.FIELD_VERSION_ID, String.valueOf(topologySource.getVersionId())); for (TopologySourceStreamMapping mapping : listTopologySourceStreamMapping( ImmutableList.of(qp1, qp2))) { streamIds.add(mapping.getStreamId()); } } return streamIds; } private Collection<TopologySourceStreamMapping> listTopologySourceStreamMapping(List<QueryParam> params) { try { return dao.find(TOPOLOGY_SOURCE_STREAM_MAPPING_NAMESPACE, params); } catch (Exception ex) { throw new RuntimeException(ex); } } private void fillProcessorStreams(TopologyProcessor processor) { if (processor != null) { fillProcessorStreams(Collections.singletonList(processor)); } } private Collection<TopologyProcessor> fillProcessorStreams(Collection<TopologyProcessor> processors) { if (processors != null) { for (TopologyProcessor processor : processors) { List<TopologyStream> topologyStreams = getOutputStreams(processor); processor.setOutputStreams(topologyStreams); processor.setOutputStreamIds(new ArrayList<>( Collections2.transform(topologyStreams, new Function<TopologyStream, Long>() { @Nullable @Override public Long apply(@Nullable TopologyStream input) { return input.getId(); } }))); } } return processors; } private List<TopologyStream> getOutputStreams(TopologyProcessor topologyProcessor) { List<TopologyStream> streams = new ArrayList<>(); if (topologyProcessor != null) { QueryParam qp1 = new QueryParam(TopologyProcessorStreamMapping.FIELD_PROCESSOR_ID, String.valueOf(topologyProcessor.getId())); QueryParam qp2 = new QueryParam(TopologyProcessorStreamMapping.FIELD_VERSION_ID, String.valueOf(topologyProcessor.getVersionId())); for (TopologyProcessorStreamMapping mapping : listTopologyProcessorStreamMapping( ImmutableList.of(qp1, qp2))) { TopologyStream topologyStream = getStreamInfo(topologyProcessor.getTopologyId(), mapping.getStreamId(), topologyProcessor.getVersionId()); if (topologyStream != null) { streams.add(topologyStream); } } } return streams; } private void fillSourceStreams(TopologySource source) { if (source != null) { fillSourceStreams(Collections.singletonList(source)); } } private Collection<TopologySource> fillSourceStreams(Collection<TopologySource> sources) { if (sources != null) { for (TopologySource source : sources) { List<TopologyStream> topologyStreams = getOutputStreams(source); source.setOutputStreams(topologyStreams); source.setOutputStreamIds(new ArrayList<>( Collections2.transform(topologyStreams, new Function<TopologyStream, Long>() { @Nullable @Override public Long apply(@Nullable TopologyStream input) { return input.getId(); } }))); } } return sources; } private List<TopologyStream> getOutputStreams(TopologySource topologySource) { List<TopologyStream> streams = new ArrayList<>(); if (topologySource != null) { QueryParam qp1 = new QueryParam(TopologySourceStreamMapping.FIELD_SOURCE_ID, String.valueOf(topologySource.getId())); QueryParam qp2 = new QueryParam(TopologySourceStreamMapping.FIELD_VERSION_ID, String.valueOf(topologySource.getVersionId())); for (TopologySourceStreamMapping mapping : listTopologySourceStreamMapping( ImmutableList.of(qp1, qp2))) { TopologyStream topologyStream = getStreamInfo(topologySource.getTopologyId(), mapping.getStreamId(), topologySource.getVersionId()); if (topologyStream != null) { streams.add(topologyStream); } } } return streams; } public TopologySink getTopologySink(Long topologyId, Long sinkId) { return getTopologySink(topologyId, sinkId, getCurrentVersionId(topologyId)); } public TopologySink getTopologySink(Long topologyId, Long sinkId, Long versionId) { TopologySink topologySink = new TopologySink(); topologySink.setId(sinkId); topologySink.setVersionId(versionId); TopologySink sink = dao.get(new StorableKey(TOPOLOGY_SINK_NAMESPACE, topologySink.getPrimaryKey())); if (sink == null || !sink.getTopologyId().equals(topologyId)) { return null; } sink.setVersionTimestamp(getVersionTimestamp(versionId)); return sink; } public TopologySink addTopologySink(Long topologyId, TopologySink topologySink) { return addTopologySink(topologyId, getCurrentVersionId(topologyId), topologySink); } public TopologySink addTopologySink(Long topologyId, Long versionId, TopologySink topologySink) { if (topologySink.getId() == null) { topologySink.setId(getNextTopologyComponentId()); } topologySink.setVersionId(versionId); topologySink.setTopologyId(topologyId); validateTopologySink(topologySink); dao.add(topologySink); topologySink.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); return topologySink; } public TopologySink addOrUpdateTopologySink(Long topologyId, Long id, TopologySink topologySink) { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologySink.setId(id); topologySink.setVersionId(currentTopologyVersionId); topologySink.setTopologyId(topologyId); validateTopologySink(topologySink); dao.addOrUpdate(topologySink); topologySink.setVersionTimestamp(updateVersionTimestamp(currentTopologyVersionId).getTimestamp()); return topologySink; } public TopologySink removeTopologySink(Long id) { TopologySink topologySink = new TopologySink(); topologySink.setId(id); return dao.remove(new StorableKey(TOPOLOGY_SINK_NAMESPACE, topologySink.getPrimaryKey())); } public TopologySink removeTopologySink(Long topologyId, Long sinkId, boolean removeEdges) { return removeTopologySink(topologyId, sinkId, getCurrentVersionId(topologyId), removeEdges); } public TopologySink removeTopologySink(Long topologyId, Long sinkId, Long versionId, boolean removeEdges) { TopologySink topologySink = getTopologySink(topologyId, sinkId, versionId); if (topologySink != null) { if (removeEdges) { removeAllEdges(topologySink); } topologySink = dao .<TopologySink>remove(new StorableKey(TOPOLOGY_SINK_NAMESPACE, topologySink.getPrimaryKey())); topologySink.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologySink; } public Collection<TopologySink> listTopologySinks() { return dao.list(TOPOLOGY_SINK_NAMESPACE); } public Collection<TopologySink> listTopologySinks(List<QueryParam> params) { return dao.find(TOPOLOGY_SINK_NAMESPACE, params); } public TopologyProcessor getTopologyProcessor(Long topologyId, Long processorId) { return getTopologyProcessor(topologyId, processorId, getCurrentVersionId(topologyId)); } public TopologyProcessor getTopologyProcessor(Long topologyId, Long processorId, Long versionId) { TopologyProcessor topologyProcessor = new TopologyProcessor(); topologyProcessor.setId(processorId); topologyProcessor.setVersionId(versionId); TopologyProcessor processor = dao .get(new StorableKey(TOPOLOGY_PROCESSOR_NAMESPACE, topologyProcessor.getPrimaryKey())); if (processor == null || !processor.getTopologyId().equals(topologyId)) { return null; } fillProcessorStreams(processor); processor.setVersionTimestamp(getVersionTimestamp(versionId)); return processor; } public TopologyProcessor addTopologyProcessor(Long topologyId, TopologyProcessor topologyProcessor) { return addTopologyProcessor(topologyId, getCurrentVersionId(topologyId), topologyProcessor); } public TopologyProcessor addTopologyProcessor(Long topologyId, Long versionId, TopologyProcessor topologyProcessor) { if (topologyProcessor.getId() == null) { topologyProcessor.setId(getNextTopologyComponentId()); } topologyProcessor.setVersionId(versionId); topologyProcessor.setTopologyId(topologyId); validateTopologyProcessor(topologyProcessor); List<TopologyStream> topologyStreams = addTopologyOutputComponent(topologyProcessor); addProcessorStreamMapping(topologyProcessor, topologyProcessor.getOutputStreamIds()); topologyProcessor.setOutputStreams(topologyStreams); topologyProcessor.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); return topologyProcessor; } public TopologyProcessor addOrUpdateTopologyProcessor(Long topologyId, Long id, TopologyProcessor topologyProcessor) { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologyProcessor.setId(id); topologyProcessor.setVersionId(currentTopologyVersionId); topologyProcessor.setTopologyId(topologyId); validateTopologyProcessor(topologyProcessor); dao.addOrUpdate(topologyProcessor); List<Long> newList = Collections.emptyList(); if (topologyProcessor.getOutputStreamIds() != null) { newList = topologyProcessor.getOutputStreamIds(); } else if (topologyProcessor.getOutputStreams() != null) { newList = updateOutputStreams(topologyProcessor); } List<Long> existing = getOutputStreamIds(topologyProcessor); Sets.SetView<Long> streamIdsToRemove = Sets.difference(ImmutableSet.copyOf(existing), ImmutableSet.copyOf(newList)); Sets.SetView<Long> streamIdsToAdd = Sets.difference(ImmutableSet.copyOf(newList), ImmutableSet.copyOf(existing)); removeProcessorStreamMapping(topologyProcessor, Lists.newArrayList(streamIdsToRemove)); addProcessorStreamMapping(topologyProcessor, Lists.newArrayList(streamIdsToAdd)); TopologyProcessor updatedProcessor = getTopologyProcessor(topologyId, id, currentTopologyVersionId); updatedProcessor.setVersionTimestamp(updateVersionTimestamp(currentTopologyVersionId).getTimestamp()); return topologyProcessor; } public TopologyProcessor removeTopologyProcessor(Long topologyId, Long processorId, boolean removeEdges) { return removeTopologyProcessor(topologyId, processorId, getCurrentVersionId(topologyId), removeEdges); } public TopologyProcessor removeTopologyProcessor(Long topologyId, Long processorId, Long versionId, boolean removeEdges) { TopologyProcessor topologyProcessor = getTopologyProcessor(topologyId, processorId, versionId); if (topologyProcessor != null) { if (removeEdges) { removeAllEdges(topologyProcessor); } removeProcessorStreamMapping(topologyProcessor); topologyProcessor = dao.<TopologyProcessor>remove( new StorableKey(TOPOLOGY_PROCESSOR_NAMESPACE, topologyProcessor.getPrimaryKey())); topologyProcessor.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologyProcessor; } public Collection<TopologyProcessor> listTopologyProcessors() { return fillProcessorStreams(dao.<TopologyProcessor>list(TOPOLOGY_PROCESSOR_NAMESPACE)); } public Collection<TopologyProcessor> listTopologyProcessors(List<QueryParam> params) { return fillProcessorStreams(dao.<TopologyProcessor>find(TOPOLOGY_PROCESSOR_NAMESPACE, params)); } private void createProcessorStreamMapping(TopologyProcessor topologyProcessor, List<TopologyStream> streams) { for (TopologyStream outputStream : streams) { TopologyStream addedStream = addStreamInfo(topologyProcessor.getTopologyId(), outputStream); dao.<TopologyProcessorStreamMapping>add(new TopologyProcessorStreamMapping(topologyProcessor.getId(), topologyProcessor.getVersionId(), addedStream.getId())); } } private void addProcessorStreamMapping(TopologyProcessor topologyProcessor, List<Long> streamIds) { for (Long outputStreamId : streamIds) { dao.<TopologyProcessorStreamMapping>add(new TopologyProcessorStreamMapping(topologyProcessor.getId(), topologyProcessor.getVersionId(), outputStreamId)); } } private void removeProcessorStreamMapping(TopologyProcessor topologyProcessor) { if (topologyProcessor != null) { removeProcessorStreamMapping(topologyProcessor, topologyProcessor.getOutputStreamIds()); } } private void removeProcessorStreamMapping(TopologyProcessor topologyProcessor, List<Long> streamIds) { if (topologyProcessor != null) { for (Long outputStreamId : streamIds) { TopologyProcessorStreamMapping mapping = new TopologyProcessorStreamMapping( topologyProcessor.getId(), topologyProcessor.getVersionId(), outputStreamId); dao.<TopologyProcessorStreamMapping>remove(mapping.getStorableKey()); } } } private List<Long> getOutputStreamIds(TopologyProcessor topologyProcessor) { List<Long> streamIds = new ArrayList<>(); if (topologyProcessor != null) { QueryParam qp1 = new QueryParam(TopologyProcessorStreamMapping.FIELD_PROCESSOR_ID, String.valueOf(topologyProcessor.getId())); QueryParam qp2 = new QueryParam(TopologyProcessorStreamMapping.FIELD_VERSION_ID, String.valueOf(topologyProcessor.getVersionId())); for (TopologyProcessorStreamMapping mapping : listTopologyProcessorStreamMapping( ImmutableList.of(qp1, qp2))) { streamIds.add(mapping.getStreamId()); } } return streamIds; } private Collection<TopologyProcessorStreamMapping> listTopologyProcessorStreamMapping(List<QueryParam> params) { try { return dao.find(TOPOLOGY_PROCESSOR_STREAM_MAPPING_NAMESPACE, params); } catch (Exception ex) { throw new RuntimeException(ex); } } public TopologyEdge getTopologyEdge(Long topologyId, Long edgeId) { return getTopologyEdge(topologyId, edgeId, getCurrentVersionId(topologyId)); } public TopologyEdge getTopologyEdge(Long topologyId, Long edgeId, Long versionId) { TopologyEdge topologyEdge = new TopologyEdge(); topologyEdge.setId(edgeId); topologyEdge.setVersionId(versionId); TopologyEdge edge = dao.get(new StorableKey(TOPOLOGY_EDGE_NAMESPACE, topologyEdge.getPrimaryKey())); if (edge == null || !edge.getTopologyId().equals(topologyId)) { return null; } edge.setVersionTimestamp(getVersionTimestamp(versionId)); return edge; } public TopologyEdge addTopologyEdge(Long topologyId, TopologyEdge topologyEdge) { return addTopologyEdge(topologyId, getCurrentVersionId(topologyId), topologyEdge); } public TopologyEdge addTopologyEdge(Long topologyId, Long versionId, TopologyEdge topologyEdge) { if (topologyEdge.getId() == null) { topologyEdge.setId(dao.nextId(TOPOLOGY_EDGE_NAMESPACE)); } topologyEdge.setVersionId(versionId); topologyEdge.setTopologyId(topologyId); validateEdge(topologyEdge); checkDuplicateEdge(topologyEdge); dao.add(topologyEdge); topologyEdge.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); return topologyEdge; } // validate from, to and stream ids of the edge private void validateEdge(TopologyEdge edge) { TopologySource source = getTopologySource(edge.getTopologyId(), edge.getFromId(), edge.getVersionId()); TopologyProcessor processor = getTopologyProcessor(edge.getTopologyId(), edge.getFromId(), edge.getVersionId()); if ((source == null || !source.getTopologyId().equals(edge.getTopologyId())) && (processor == null || !processor.getTopologyId().equals(edge.getTopologyId()))) { throw new IllegalArgumentException("Invalid source for edge " + edge); } TopologyOutputComponent outputComponent = source != null ? source : processor; processor = getTopologyProcessor(edge.getTopologyId(), edge.getToId(), edge.getVersionId()); TopologySink sink = getTopologySink(edge.getTopologyId(), edge.getToId(), edge.getVersionId()); if ((processor == null || !processor.getTopologyId().equals(edge.getTopologyId())) && (sink == null || !sink.getTopologyId().equals(edge.getTopologyId()))) { throw new IllegalArgumentException("Invalid destination for edge " + edge); } Set<Long> outputStreamIds = new HashSet<>(); if (outputComponent.getOutputStreamIds() != null) { outputStreamIds.addAll(outputComponent.getOutputStreamIds()); } else if (outputComponent.getOutputStreams() != null) { outputStreamIds.addAll(Collections2.transform(outputComponent.getOutputStreams(), new Function<TopologyStream, Long>() { @Override public Long apply(TopologyStream input) { return input.getId(); } })); } Collection<Long> edgeStreamIds = Collections2.transform(edge.getStreamGroupings(), new Function<StreamGrouping, Long>() { public Long apply(StreamGrouping streamGrouping) { return streamGrouping.getStreamId(); } }); if (!outputStreamIds.containsAll(edgeStreamIds)) { throw new IllegalArgumentException( "Edge stream Ids " + edgeStreamIds + " must be a subset of outputStreamIds " + outputStreamIds); } // check the fields specified in the fields grouping is a subset of the stream fields for (StreamGrouping streamGrouping : edge.getStreamGroupings()) { List<String> fields; if ((fields = streamGrouping.getFields()) != null) { Set<String> schemaFieldPatterns = getFieldPatterns( getStreamInfo(edge.getTopologyId(), streamGrouping.getStreamId(), edge.getVersionId()) .getFields()); fields.forEach(field -> { schemaFieldPatterns.stream().filter(pat -> field.matches(pat)).findAny() .orElseThrow(() -> new IllegalArgumentException("Fields in the grouping " + fields + " must be a subset the stream fields " + schemaFieldPatterns)); }); } } } private Set<String> getFieldPatterns(List<Schema.Field> fields) { if (fields == null || fields.isEmpty()) { return Collections.emptySet(); } Set<String> names = new HashSet<>(); fields.forEach(field -> { if (field.getType() == Schema.Type.NESTED) { getFieldPatterns(((Schema.NestedField) field).getFields()) .forEach(childFieldName -> names.add(field.getName() + "." + childFieldName)); } else if (field.getType() == Schema.Type.ARRAY) { String pattern; if (field.getName() == null || field.getName().isEmpty()) { pattern = "\\[\\d+\\]"; } else { pattern = field.getName() + "\\[\\d+\\]"; } ((Schema.ArrayField) field).getMembers().forEach(member -> { getFieldPatterns(Collections.singletonList(member)).forEach(pat -> { if (member.getType() == Schema.Type.ARRAY) { names.add(pattern + pat); } else if (member.getType() == Schema.Type.NESTED) { names.add(pattern + "." + pat); } }); }); } else { names.add(field.getName()); } }); LOG.debug("Field names {}", names); return names; } // check if edge already exists for given topology between same source and dest private void checkDuplicateEdge(TopologyEdge edge) { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam(TopologyEdge.TOPOLOGYID, edge.getTopologyId().toString())); queryParams.add(new QueryParam(TopologyEdge.VERSIONID, edge.getVersionId().toString())); queryParams.add(new QueryParam(TopologyEdge.FROMID, edge.getFromId().toString())); queryParams.add(new QueryParam(TopologyEdge.TOID, edge.getToId().toString())); try { Collection<TopologyEdge> existingEdges = listTopologyEdges(queryParams); if (existingEdges != null && !existingEdges.isEmpty()) { throw new IllegalArgumentException( "Edge already exists between source and destination, use update api"); } } catch (Exception ex) { throw new RuntimeException(ex); } } public TopologyEdge addOrUpdateTopologyEdge(Long topologyId, Long id, TopologyEdge topologyEdge) { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologyEdge.setId(id); topologyEdge.setVersionId(currentTopologyVersionId); topologyEdge.setTopologyId(topologyId); validateEdge(topologyEdge); dao.addOrUpdate(topologyEdge); topologyEdge.setVersionTimestamp(updateVersionTimestamp(currentTopologyVersionId).getTimestamp()); return topologyEdge; } public TopologyEdge removeTopologyEdge(Long topologyId, Long edgeId) { return removeTopologyEdge(topologyId, edgeId, getCurrentVersionId(topologyId)); } public TopologyEdge removeTopologyEdge(Long topologyId, Long edgeId, Long versionId) { TopologyEdge topologyEdge = getTopologyEdge(topologyId, edgeId, versionId); if (topologyEdge != null) { topologyEdge = dao.remove(new StorableKey(TOPOLOGY_EDGE_NAMESPACE, topologyEdge.getPrimaryKey())); topologyEdge.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologyEdge; } public Collection<TopologyEdge> listTopologyEdges() { return dao.list(TOPOLOGY_EDGE_NAMESPACE); } public Collection<TopologyEdge> listTopologyEdges(List<QueryParam> params) throws Exception { return dao.find(TOPOLOGY_EDGE_NAMESPACE, params); } public TopologyStream getStreamInfo(Long topologyId, Long streamId) { return getStreamInfo(topologyId, streamId, getCurrentVersionId(topologyId)); } public TopologyStream getStreamInfo(Long topologyId, Long streamId, Long versionId) { TopologyStream topologyStream = new TopologyStream(); topologyStream.setId(streamId); topologyStream.setVersionId(versionId); TopologyStream result = dao.get(new StorableKey(STREAMINFO_NAMESPACE, topologyStream.getPrimaryKey())); if (result == null || !result.getTopologyId().equals(topologyId)) { return null; } result.setVersionTimestamp(getVersionTimestamp(versionId)); return result; } public TopologyStream getStreamInfoByName(Long topologyId, String streamId) { return getStreamInfoByName(topologyId, streamId, getCurrentVersionId(topologyId)); } public TopologyStream getStreamInfoByName(Long topologyId, String streamId, Long versionId) { List<QueryParam> queryParams = WSUtils.buildTopologyIdAndVersionIdAwareQueryParams(topologyId, versionId, null); try { for (TopologyStream topologyStream : listStreamInfos(queryParams)) { if (topologyStream.getStreamId().equals(streamId)) { return topologyStream; } } } catch (Exception ex) { LOG.error("Got exception ", ex); throw new RuntimeException(ex); } return null; } private void validateStreamInfo(TopologyStream topologyStream) { if (topologyStream.getFields().isEmpty()) { throw new IllegalArgumentException("Stream with empty fields: " + topologyStream); } StorageUtils.ensureUnique(topologyStream, this::listStreamInfos, QueryParam.params(TopologyStream.TOPOLOGYID, topologyStream.getTopologyId().toString(), TopologyStream.VERSIONID, topologyStream.getVersionId().toString(), TopologyStream.STREAMID, topologyStream.getStreamId())); } public TopologyStream addStreamInfo(Long topologyId, TopologyStream topologyStream) { return addStreamInfo(topologyId, getCurrentVersionId(topologyId), topologyStream); } public TopologyStream addStreamInfo(Long topologyId, Long versionId, TopologyStream topologyStream) { if (topologyStream.getId() == null) { topologyStream.setId(dao.nextId(STREAMINFO_NAMESPACE)); } long timestamp = System.currentTimeMillis(); topologyStream.setVersionTimestamp(timestamp); topologyStream.setVersionId(versionId); topologyStream.setTopologyId(topologyId); validateStreamInfo(topologyStream); dao.add(topologyStream); updateVersionTimestamp(versionId, timestamp); return topologyStream; } public TopologyStream addOrUpdateStreamInfo(Long topologyId, Long id, TopologyStream stream) { stream.setId(id); Long currentVersionId = getCurrentVersionId(topologyId); stream.setVersionId(currentVersionId); stream.setTopologyId(topologyId); long timestamp = System.currentTimeMillis(); stream.setVersionTimestamp(timestamp); validateStreamInfo(stream); dao.addOrUpdate(stream); updateVersionTimestamp(currentVersionId, timestamp); return stream; } public TopologyStream removeStreamInfo(Long topologyId, Long streamId) { return removeStreamInfo(topologyId, streamId, getCurrentVersionId(topologyId)); } public TopologyStream removeStreamInfo(Long topologyId, Long streamId, Long versionId) { TopologyStream topologyStream = getStreamInfo(topologyId, streamId, versionId); if (topologyStream != null) { topologyStream = dao.remove(new StorableKey(STREAMINFO_NAMESPACE, topologyStream.getPrimaryKey())); topologyStream.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologyStream; } public Collection<TopologyStream> listStreamInfos() { return dao.list(STREAMINFO_NAMESPACE); } public Collection<TopologyStream> listStreamInfos(List<QueryParam> params) { return dao.find(STREAMINFO_NAMESPACE, params); } public Collection<TopologyRule> listRules() { return dao.list(TOPOLOGY_RULEINFO_NAMESPACE); } public Collection<TopologyRule> listRules(List<QueryParam> params) throws Exception { return dao.find(TOPOLOGY_RULEINFO_NAMESPACE, params); } public TopologyRule addRule(Long topologyId, TopologyRule topologyRule) throws Exception { return addRule(topologyId, getCurrentVersionId(topologyId), topologyRule); } public TopologyRule addRule(Long topologyId, Long versionId, TopologyRule topologyRule) throws Exception { if (topologyRule.getId() == null) { topologyRule.setId(dao.nextId(TOPOLOGY_RULEINFO_NAMESPACE)); } topologyRule.setVersionId(versionId); topologyRule.setTopologyId(topologyId); String parsedRuleStr = parseAndSerialize(topologyRule); LOG.debug("ParsedRuleStr {}", parsedRuleStr); topologyRule.setParsedRuleStr(parsedRuleStr); dao.add(topologyRule); topologyRule.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); return topologyRule; } public TopologyRule getRule(Long topologyId, Long ruleId) throws Exception { return getRule(topologyId, ruleId, getCurrentVersionId(topologyId)); } public TopologyRule getRule(Long topologyId, Long ruleId, Long versionId) throws Exception { TopologyRule topologyTopologyRule = new TopologyRule(); topologyTopologyRule.setId(ruleId); topologyTopologyRule.setVersionId(versionId); TopologyRule ruleInfo = dao .get(new StorableKey(TOPOLOGY_RULEINFO_NAMESPACE, topologyTopologyRule.getPrimaryKey())); if (ruleInfo == null || !ruleInfo.getTopologyId().equals(topologyId)) { return null; } ruleInfo.setVersionTimestamp(getVersionTimestamp(versionId)); return ruleInfo; } public TopologyRule addOrUpdateRule(Long topologyId, Long ruleId, TopologyRule topologyRule) throws Exception { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologyRule.setId(ruleId); topologyRule.setVersionId(currentTopologyVersionId); topologyRule.setTopologyId(topologyId); String parsedRuleStr = parseAndSerialize(topologyRule); LOG.debug("ParsedRuleStr {}", parsedRuleStr); topologyRule.setParsedRuleStr(parsedRuleStr); dao.addOrUpdate(topologyRule); topologyRule.setVersionTimestamp(updateVersionTimestamp(currentTopologyVersionId).getTimestamp()); return topologyRule; } public TopologyRule removeRule(Long topologyId, Long ruleId) throws Exception { return removeRule(topologyId, ruleId, getCurrentVersionId(topologyId)); } public TopologyRule removeRule(Long topologyId, Long ruleId, Long versionId) throws Exception { TopologyRule topologyRule = getRule(topologyId, ruleId, versionId); if (topologyRule != null) { topologyRule = dao.remove(new StorableKey(TOPOLOGY_RULEINFO_NAMESPACE, topologyRule.getPrimaryKey())); topologyRule.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologyRule; } public Collection<TopologyWindow> listWindows() { return dao.list(TOPOLOGY_WINDOWINFO_NAMESPACE); } public Collection<TopologyWindow> listWindows(List<QueryParam> params) throws Exception { return dao.find(TOPOLOGY_WINDOWINFO_NAMESPACE, params); } public Collection<TopologyBranchRule> listBranchRules() { return dao.list(TOPOLOGY_BRANCHRULEINFO_NAMESPACE); } public Collection<TopologyBranchRule> listBranchRules(List<QueryParam> params) throws Exception { return dao.find(TOPOLOGY_BRANCHRULEINFO_NAMESPACE, params); } public TopologyBranchRule addBranchRule(Long topologyId, TopologyBranchRule topologyBranchRule) throws Exception { return addBranchRule(topologyId, getCurrentVersionId(topologyId), topologyBranchRule); } public TopologyBranchRule addBranchRule(Long topologyId, Long versionId, TopologyBranchRule topologyBranchRule) throws Exception { if (topologyBranchRule.getId() == null) { topologyBranchRule.setId(dao.nextId(TOPOLOGY_BRANCHRULEINFO_NAMESPACE)); } topologyBranchRule.setTopologyId(topologyId); topologyBranchRule.setVersionId(versionId); String parsedRuleStr = parseAndSerialize(topologyBranchRule); LOG.debug("ParsedRuleStr {}", parsedRuleStr); topologyBranchRule.setParsedRuleStr(parsedRuleStr); dao.add(topologyBranchRule); topologyBranchRule.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); return topologyBranchRule; } public TopologyBranchRule getBranchRule(Long topologyId, Long ruleId) throws Exception { return getBranchRule(topologyId, ruleId, getCurrentVersionId(topologyId)); } public TopologyBranchRule getBranchRule(Long topologyId, Long ruleId, Long versionId) throws Exception { TopologyBranchRule topologyBranchRule = new TopologyBranchRule(); topologyBranchRule.setId(ruleId); topologyBranchRule.setVersionId(versionId); topologyBranchRule = dao .get(new StorableKey(TOPOLOGY_BRANCHRULEINFO_NAMESPACE, topologyBranchRule.getPrimaryKey())); if (topologyBranchRule == null || !topologyBranchRule.getTopologyId().equals(topologyId)) { return null; } topologyBranchRule.setVersionTimestamp(getVersionTimestamp(versionId)); return topologyBranchRule; } public TopologyBranchRule addOrUpdateBranchRule(Long topologyId, Long ruleId, TopologyBranchRule topologyBranchRule) throws Exception { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologyBranchRule.setId(ruleId); topologyBranchRule.setVersionId(currentTopologyVersionId); topologyBranchRule.setTopologyId(topologyId); String parsedRuleStr = parseAndSerialize(topologyBranchRule); LOG.debug("ParsedRuleStr {}", parsedRuleStr); topologyBranchRule.setParsedRuleStr(parsedRuleStr); dao.addOrUpdate(topologyBranchRule); topologyBranchRule.setVersionTimestamp(updateVersionTimestamp(currentTopologyVersionId).getTimestamp()); return topologyBranchRule; } public TopologyBranchRule removeBranchRule(Long topologyId, Long id) throws Exception { return removeBranchRule(topologyId, id, getCurrentVersionId(topologyId)); } public TopologyBranchRule removeBranchRule(Long topologyId, Long id, Long versionId) throws Exception { TopologyBranchRule topologyBranchRule = getBranchRule(topologyId, id, versionId); if (topologyBranchRule != null) { topologyBranchRule = dao .remove(new StorableKey(TOPOLOGY_BRANCHRULEINFO_NAMESPACE, topologyBranchRule.getPrimaryKey())); topologyBranchRule.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologyBranchRule; } public TopologyWindow addWindow(Long topologyId, TopologyWindow topologyWindow) throws Exception { return addWindow(topologyId, getCurrentVersionId(topologyId), topologyWindow); } public TopologyWindow addWindow(Long topologyId, Long versionId, TopologyWindow topologyWindow) throws Exception { if (topologyWindow.getId() == null) { topologyWindow.setId(dao.nextId(TOPOLOGY_WINDOWINFO_NAMESPACE)); } topologyWindow.setTopologyId(topologyId); topologyWindow.setVersionId(versionId); String parsedRuleStr = parseAndSerialize(topologyWindow); LOG.debug("ParsedRuleStr {}", parsedRuleStr); topologyWindow.setParsedRuleStr(parsedRuleStr); dao.add(topologyWindow); topologyWindow.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); return topologyWindow; } public TopologyWindow getWindow(Long topologyId, Long windowId) throws Exception { return getWindow(topologyId, windowId, getCurrentVersionId(topologyId)); } public TopologyWindow getWindow(Long topologyId, Long windowId, Long versionId) throws Exception { TopologyWindow topologyTopologyWindow = new TopologyWindow(); topologyTopologyWindow.setId(windowId); topologyTopologyWindow.setVersionId(versionId); TopologyWindow windowInfo = dao .get(new StorableKey(TOPOLOGY_WINDOWINFO_NAMESPACE, topologyTopologyWindow.getPrimaryKey())); if (windowInfo == null || !windowInfo.getTopologyId().equals(topologyId)) { return null; } windowInfo.setVersionTimestamp(getVersionTimestamp(versionId)); return windowInfo; } public TopologyWindow addOrUpdateWindow(Long topologyId, Long windowId, TopologyWindow topologyWindow) throws Exception { Long currentTopologyVersionId = getCurrentVersionId(topologyId); topologyWindow.setId(windowId); topologyWindow.setVersionId(currentTopologyVersionId); topologyWindow.setTopologyId(topologyId); String parsedRuleStr = parseAndSerialize(topologyWindow); LOG.debug("ParsedRuleStr {}", parsedRuleStr); topologyWindow.setParsedRuleStr(parsedRuleStr); dao.addOrUpdate(topologyWindow); topologyWindow.setVersionTimestamp(updateVersionTimestamp(currentTopologyVersionId).getTimestamp()); return topologyWindow; } public TopologyWindow removeWindow(Long topologyId, Long windowId) throws Exception { return removeWindow(topologyId, windowId, getCurrentVersionId(topologyId)); } public TopologyWindow removeWindow(Long topologyId, Long windowId, Long versionId) throws Exception { TopologyWindow topologyWindow = getWindow(topologyId, windowId, versionId); if (topologyWindow != null) { topologyWindow = dao .remove(new StorableKey(TOPOLOGY_WINDOWINFO_NAMESPACE, topologyWindow.getPrimaryKey())); topologyWindow.setVersionTimestamp(updateVersionTimestamp(versionId).getTimestamp()); } return topologyWindow; } private String parseAndSerialize(TopologyRule topologyRule) throws JsonProcessingException { Rule rule = new Rule(); rule.setId(topologyRule.getId()); rule.setName(topologyRule.getName()); rule.setDescription(topologyRule.getDescription()); rule.setWindow(topologyRule.getWindow()); rule.setActions(topologyRule.getActions()); if (topologyRule.getStreams() != null && !topologyRule.getStreams().isEmpty()) { topologyRule.setSql(getSqlString(topologyRule.getStreams(), topologyRule.getProjections(), topologyRule.getCondition(), null)); } else if (StringUtils.isEmpty(topologyRule.getSql())) { throw new IllegalArgumentException("Either streams or sql string should be specified."); } updateRuleWithSql(rule, topologyRule.getSql(), topologyRule.getTopologyId(), topologyRule.getVersionId()); ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(rule); } private String parseAndSerialize(TopologyBranchRule ruleInfo) throws JsonProcessingException { Rule rule = new Rule(); rule.setId(ruleInfo.getId()); rule.setName(ruleInfo.getName()); rule.setDescription(ruleInfo.getDescription()); rule.setActions(ruleInfo.getActions()); String sql = getSqlString(Collections.singletonList(ruleInfo.getStream()), null, ruleInfo.getCondition(), null); updateRuleWithSql(rule, sql, ruleInfo.getTopologyId(), ruleInfo.getVersionId()); ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(rule); } private String parseAndSerialize(TopologyWindow topologyWindow) throws JsonProcessingException { if (topologyWindow.getStreams() == null || topologyWindow.getStreams().isEmpty()) { LOG.error("Streams should be specified."); return StringUtils.EMPTY; } Rule rule = new Rule(); rule.setId(topologyWindow.getId()); rule.setName(topologyWindow.getName()); rule.setDescription(topologyWindow.getDescription()); rule.setWindow(topologyWindow.getWindow()); rule.setActions(topologyWindow.getActions()); String sql = getSqlString(topologyWindow.getStreams(), topologyWindow.getProjections(), topologyWindow.getCondition(), topologyWindow.getGroupbykeys()); updateRuleWithSql(rule, sql, topologyWindow.getTopologyId(), topologyWindow.getVersionId()); ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(rule); } String getSqlString(List<String> streams, List<Projection> projections, String condition, List<String> groupByKeys) { String SQL = select(streams, projections).orElse("SELECT * "); SQL += join(" FROM ", getTable(streams)).get(); SQL += join(" WHERE ", convertNested(streams, condition)).orElse(""); SQL += join(" GROUP BY ", convertNested(streams, groupByKeys)).orElse(""); return SQL; } /* Converts streamline dot(.) separated nested expr to calcite format '[]'. Dot immediately following a stream name are retained. E.g. f1.g.h = 'A' and kafka_stream_1.f2[5].j = 100 TO f1['g']['h'] = 'A' and kafka_stream_1.f2[5]['j'] = 100 In the above example 'kafka_stream_1' is the input stream and the dot immediately following it is retained. */ String convertNested(List<String> streams, String expr) { if (StringUtils.isEmpty(expr)) { return expr; } StringBuilder sb = new StringBuilder(); Pattern pattern = Pattern.compile("(\\w+)?\\.(\\w+)"); Matcher matcher = pattern.matcher(expr); int startFrom = 0; int end = 0; while (matcher.find(startFrom)) { String prefix = matcher.group(1); String suffix = matcher.group(2); if (end < matcher.start()) { sb.append(expr.substring(end, matcher.start())); } if (streams.contains(prefix)) { sb.append(matcher.group()); } else { if (startFrom == 0) { sb.append(prefix); } sb.append("['").append(suffix).append("']"); } startFrom = matcher.start(2); end = matcher.end(); } sb.append(expr.substring(end)); return sb.toString(); } private List<String> convertNested(List<String> streams, List<String> exprs) { if (exprs == null || exprs.isEmpty()) { return exprs; } return exprs.stream().map(x -> convertNested(streams, x)).collect(Collectors.toList()); } private Optional<String> select(List<String> streams, List<Projection> projections) { if (projections != null) { return join("SELECT ", projections.stream().map(p -> { Projection res = new Projection(convertNested(streams, p.getExpr()), p.getFunctionName(), convertNested(streams, p.getArgs()), p.getOutputFieldName()); return res.toString(); }).collect(Collectors.toList())); } return Optional.empty(); } private Optional<String> join(String keyword, String part) { if (part != null) { return join(keyword, Collections.singletonList(part)); } return Optional.empty(); } private Optional<String> join(String keyword, Collection<String> parts) { if (parts != null && !parts.isEmpty()) { return Optional.of(keyword + Joiner.on(",").join(parts)); } return Optional.empty(); } private String getTable(List<String> streams) { if (streams == null || streams.isEmpty()) { LOG.warn("Rule condition is specified without input stream, will use default stream"); return StreamlineEvent.DEFAULT_SOURCE_STREAM; } else if (streams.size() == 1) { return streams.iterator().next(); } else { throw new UnsupportedOperationException("Joining multiple streams"); } } private void updateRuleWithSql(Rule rule, String sql, Long topologyId, Long versionId) { // parse RuleParser ruleParser = new RuleParser(this, sql, topologyId, versionId); ruleParser.parse(); // update rule with parsed sql constructs rule.setStreams( new HashSet<>(Collections2.transform(ruleParser.getStreams(), new Function<Stream, String>() { @Override public String apply(Stream input) { return input.getId(); } }))); rule.setProjection(ruleParser.getProjection()); rule.setCondition(ruleParser.getCondition()); rule.setGroupBy(ruleParser.getGroupBy()); rule.setHaving(ruleParser.getHaving()); rule.setReferredUdfs(ruleParser.getReferredUdfs()); } public Collection<UDF> listUDFs() { return this.dao.list(UDF_NAMESPACE); } public Collection<UDF> listUDFs(List<QueryParam> queryParams) { return dao.find(UDF_NAMESPACE, queryParams); } public UDF getUDF(Long id) { UDF udf = new UDF(); udf.setId(id); return this.dao.get(new StorableKey(UDF_NAMESPACE, udf.getPrimaryKey())); } public UDF addUDF(UDF udf) { if (udf.getId() == null) { udf.setId(this.dao.nextId(UDF_NAMESPACE)); } udf.setName(udf.getName().toUpperCase()); this.dao.add(udf); return udf; } public Map<String, Class<?>> loadUdfsFromJar(java.io.File jarFile) throws IOException { Map<String, Class<?>> udafs = new HashMap<>(); for (Class<?> udfClass : UDF_CLASSES) { for (Class<?> clazz : ProxyUtil.loadAllClassesFromJar(jarFile, udfClass)) { udafs.put(clazz.getCanonicalName(), clazz); } } return udafs; } public UDF removeUDF(Long id) { UDF udf = new UDF(); udf.setId(id); return dao.remove(new StorableKey(UDF_NAMESPACE, udf.getPrimaryKey())); } public UDF addOrUpdateUDF(Long udfId, UDF udf) { udf.setId(udfId); udf.setName(udf.getName().toUpperCase()); this.dao.addOrUpdate(udf); return udf; } private void loadTransformationClassForBundle(TopologyComponentBundle topologyComponentBundle, InputStream bundleJar) { if (topologyComponentBundle.getStreamingEngine().equals(TopologyLayoutConstants.STORM_STREAMING_ENGINE)) { if (topologyComponentBundle.getBuiltin()) { // no transformation class validations for top level topology type if (topologyComponentBundle.getType() == TopologyComponentBundle.TopologyComponentType.TOPOLOGY) { return; } try { FluxComponent fluxComponent = (FluxComponent) Class .forName(topologyComponentBundle.getTransformationClass()).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { LOG.debug("Got exception", e); throw new RuntimeException( "Check transformationClass property. Cannot load builtin transformation class " + topologyComponentBundle.getTransformationClass()); } } else { ProxyUtil<FluxComponent> fluxComponentProxyUtil = new ProxyUtil<FluxComponent>(FluxComponent.class); OutputStream os = null; InputStream is = null; try { java.io.File tmpFile = java.io.File.createTempFile(UUID.randomUUID().toString(), ".jar"); tmpFile.deleteOnExit(); os = new FileOutputStream(tmpFile); ByteStreams.copy(bundleJar, os); FluxComponent fluxComponent = fluxComponentProxyUtil.loadClassFromJar(tmpFile.getAbsolutePath(), topologyComponentBundle.getTransformationClass()); } catch (Exception ex) { LOG.debug("Got exception", ex); throw new RuntimeException("Cannot load transformation class " + topologyComponentBundle.getTransformationClass() + " from bundle Jar: "); } finally { try { if (os != null) { os.close(); } } catch (IOException ex) { LOG.error("Got exception", ex); } } } } } private String getTopologyComponentBundleJarName(TopologyComponentBundle topologyComponentBundle) { List<String> jarFileName = Arrays.asList(topologyComponentBundle.getStreamingEngine(), topologyComponentBundle.getType().name(), topologyComponentBundle.getSubType(), UUID.randomUUID().toString(), ".jar"); String bundleJarFileName = String.join("-", jarFileName); return bundleJarFileName; } private Collection<File> listFiles(List<QueryParam> queryParams) { return dao.find(File.NAMESPACE, queryParams); } public Collection<TopologyTestRunHistory> listTopologyTestRunHistory(Long topologyId) { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam("topologyId", String.valueOf(topologyId))); return dao.find(TopologyTestRunHistory.NAMESPACE, queryParams); } public Collection<TopologyTestRunHistory> listTopologyTestRunHistory(Long topologyId, Long versionId) { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam("topologyId", String.valueOf(topologyId))); queryParams.add(new QueryParam("versionId", String.valueOf(versionId))); return dao.find(TopologyTestRunHistory.NAMESPACE, queryParams); } public TopologyTestRunHistory getTopologyTestRunHistory(Long id) { TopologyTestRunHistory history = new TopologyTestRunHistory(); history.setId(id); return dao.get(new StorableKey(TopologyTestRunHistory.NAMESPACE, history.getPrimaryKey())); } public TopologyTestRunHistory addTopologyTestRunHistory(TopologyTestRunHistory history) { if (history.getId() == null) { history.setId(dao.nextId(TopologyTestRunHistory.NAMESPACE)); } history.setTimestamp(System.currentTimeMillis()); dao.add(history); return history; } public TopologyTestRunHistory addOrUpdateTopologyTestRunHistory(Long id, TopologyTestRunHistory history) { history.setId(id); history.setTimestamp(System.currentTimeMillis()); dao.addOrUpdate(history); return history; } public Collection<TopologyTestRunCase> listTopologyTestRunCase(Long topologyId, Long versionId) { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam("topologyId", String.valueOf(topologyId))); queryParams.add(new QueryParam("versionId", String.valueOf(versionId))); return dao.find(TopologyTestRunCase.NAMESPACE, queryParams); } public Collection<TopologyTestRunCase> listTopologyTestRunCase(List<QueryParam> queryParams) { return dao.find(TopologyTestRunCase.NAMESPACE, queryParams); } public TopologyTestRunCase getTopologyTestRunCase(Long topologyId, Long testcaseId) { TopologyTestRunCase testCase = new TopologyTestRunCase(); testCase.setId(testcaseId); TopologyTestRunCase found = dao .get(new StorableKey(TopologyTestRunCase.NAMESPACE, testCase.getPrimaryKey())); if (found == null || !found.getTopologyId().equals(topologyId)) { return null; } return found; } private TopologyTestRunCase addTopologyTestRunCase(Long topologyId, Long newVersionId, TopologyTestRunCase testRunCase) { // unlike topology related entities, just issues a new id testRunCase.setId(null); testRunCase.setTopologyId(topologyId); testRunCase.setVersionId(newVersionId); return addTopologyTestRunCase(testRunCase); } public TopologyTestRunCase addTopologyTestRunCase(TopologyTestRunCase testCase) { if (testCase.getId() == null) { testCase.setId(dao.nextId(TopologyTestRunCase.NAMESPACE)); } testCase.setTimestamp(System.currentTimeMillis()); dao.add(testCase); return testCase; } public TopologyTestRunCase addOrUpdateTopologyTestRunCase(Long id, TopologyTestRunCase testCase) { testCase.setId(id); testCase.setTimestamp(System.currentTimeMillis()); dao.addOrUpdate(testCase); return testCase; } public TopologyTestRunCase removeTestRunCase(Long topologyId, Long testcaseId) { TopologyTestRunCase testcase = getTopologyTestRunCase(topologyId, testcaseId); if (testcase != null) { testcase = dao.remove(testcase.getStorableKey()); } return testcase; } public TopologyTestRunCaseSource getTopologyTestRunCaseSourceBySourceId(Long testCaseId, Long sourceId) { TopologyTestRunCaseSource testCaseSource = new TopologyTestRunCaseSource(); testCaseSource.setId(testCaseId); Collection<TopologyTestRunCaseSource> sources = dao.find(TopologyTestRunCaseSource.NAMESPACE, Lists.newArrayList(new QueryParam("testCaseId", testCaseId.toString()), new QueryParam("sourceId", sourceId.toString()))); if (sources == null || sources.isEmpty()) { return null; } else if (sources.size() > 1) { LOG.warn("More than one test run case source entity for same test case and source. test case id: " + testCaseId + " , source id: " + sourceId); LOG.warn("Returning first one..."); } return sources.iterator().next(); } public TopologyTestRunCaseSource getTopologyTestRunCaseSource(Long testcaseId, Long id) { TopologyTestRunCaseSource testCaseSource = new TopologyTestRunCaseSource(); testCaseSource.setId(id); TopologyTestRunCaseSource retrieved = dao .get(new StorableKey(TopologyTestRunCaseSource.NAMESPACE, testCaseSource.getPrimaryKey())); if (retrieved == null || !retrieved.getTestCaseId().equals(testcaseId)) { return null; } return retrieved; } private void addTopologyTestRunCaseSource(Long newTestCaseId, Long newVersionId, TopologyTestRunCaseSource testRunCaseSource) { // unlike topology related entities, just issues a new id testRunCaseSource.setId(null); testRunCaseSource.setTestCaseId(newTestCaseId); testRunCaseSource.setVersionId(newVersionId); addTopologyTestRunCaseSource(testRunCaseSource); } public TopologyTestRunCaseSource addTopologyTestRunCaseSource(TopologyTestRunCaseSource testCaseSource) { if (testCaseSource.getId() == null) { testCaseSource.setId(dao.nextId(TopologyTestRunCaseSource.NAMESPACE)); } testCaseSource.setTimestamp(System.currentTimeMillis()); dao.add(testCaseSource); return testCaseSource; } public TopologyTestRunCaseSource addOrUpdateTopologyTestRunCaseSource(Long id, TopologyTestRunCaseSource testCaseSource) { testCaseSource.setId(id); testCaseSource.setTimestamp(System.currentTimeMillis()); dao.addOrUpdate(testCaseSource); return testCaseSource; } public TopologyTestRunCaseSource removeTestRunCaseSourceBySourceId(Long testcaseId, Long sourceId) { TopologyTestRunCaseSource testcase = getTopologyTestRunCaseSourceBySourceId(testcaseId, sourceId); if (testcase != null) { testcase = dao.remove(testcase.getStorableKey()); } return testcase; } public TopologyTestRunCaseSource removeTestRunCaseSource(Long id) { TopologyTestRunCaseSource testcaseSource = new TopologyTestRunCaseSource(); testcaseSource.setId(id); return dao.remove(testcaseSource.getStorableKey()); } public Collection<TopologyTestRunCaseSource> listTopologyTestRunCaseSource(Long topologyId, Long testCaseId) { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam("topologyId", String.valueOf(topologyId))); queryParams.add(new QueryParam("testCaseId", String.valueOf(testCaseId))); return dao.find(TopologyTestRunCaseSource.NAMESPACE, queryParams); } public Collection<TopologyTestRunCaseSource> listTopologyTestRunCaseSource(List<QueryParam> queryParams) { return dao.find(TopologyTestRunCaseSource.NAMESPACE, queryParams); } public TopologyTestRunCaseSink getTopologyTestRunCaseSinkBySinkId(Long testCaseId, Long sinkId) { TopologyTestRunCaseSink testCaseSink = new TopologyTestRunCaseSink(); testCaseSink.setId(testCaseId); Collection<TopologyTestRunCaseSink> sinks = dao.find(TopologyTestRunCaseSink.NAMESPACE, Lists.newArrayList( new QueryParam("testCaseId", testCaseId.toString()), new QueryParam("sinkId", sinkId.toString()))); if (sinks == null || sinks.isEmpty()) { return null; } else if (sinks.size() > 1) { LOG.warn("More than one test run case sink entity for same test case and sink. test case id: " + testCaseId + " , sink id: " + sinkId); LOG.warn("Returning first one..."); } return sinks.iterator().next(); } public TopologyTestRunCaseSink getTopologyTestRunCaseSink(Long testcaseId, Long id) { TopologyTestRunCaseSink testCaseSink = new TopologyTestRunCaseSink(); testCaseSink.setId(id); TopologyTestRunCaseSink retrieved = dao .get(new StorableKey(TopologyTestRunCaseSink.NAMESPACE, testCaseSink.getPrimaryKey())); if (retrieved == null || !retrieved.getTestCaseId().equals(testcaseId)) { return null; } return retrieved; } private void addTopologyTestRunCaseSink(Long newTestCaseId, Long newVersionId, TopologyTestRunCaseSink testRunCaseSink) { // unlike topology related entities, just issues a new id testRunCaseSink.setId(null); testRunCaseSink.setTestCaseId(newTestCaseId); testRunCaseSink.setVersionId(newVersionId); addTopologyTestRunCaseSink(testRunCaseSink); } public TopologyTestRunCaseSink addTopologyTestRunCaseSink(TopologyTestRunCaseSink testCaseSink) { if (testCaseSink.getId() == null) { testCaseSink.setId(dao.nextId(TopologyTestRunCaseSink.NAMESPACE)); } testCaseSink.setTimestamp(System.currentTimeMillis()); dao.add(testCaseSink); return testCaseSink; } public TopologyTestRunCaseSink addOrUpdateTopologyTestRunCaseSink(Long id, TopologyTestRunCaseSink testCaseSink) { testCaseSink.setId(id); testCaseSink.setTimestamp(System.currentTimeMillis()); dao.addOrUpdate(testCaseSink); return testCaseSink; } public TopologyTestRunCaseSink removeTestRunCaseSinkBySinkId(Long testcaseId, Long sinkId) { TopologyTestRunCaseSink testcase = getTopologyTestRunCaseSinkBySinkId(testcaseId, sinkId); if (testcase != null) { testcase = dao.remove(testcase.getStorableKey()); } return testcase; } public TopologyTestRunCaseSink removeTestRunCaseSink(Long id) { TopologyTestRunCaseSink testcaseSink = new TopologyTestRunCaseSink(); testcaseSink.setId(id); return dao.remove(testcaseSink.getStorableKey()); } public Collection<TopologyTestRunCaseSink> listTopologyTestRunCaseSink(Long topologyId, Long testCaseId) { List<QueryParam> queryParams = new ArrayList<>(); queryParams.add(new QueryParam("topologyId", String.valueOf(topologyId))); queryParams.add(new QueryParam("testCaseId", String.valueOf(testCaseId))); return dao.find(TopologyTestRunCaseSink.NAMESPACE, queryParams); } public Collection<TopologyTestRunCaseSink> listTopologyTestRunCaseSink(List<QueryParam> queryParams) { return dao.find(TopologyTestRunCaseSink.NAMESPACE, queryParams); } }