package com.thinkbiganalytics.feedmgr.nifi.cache; /*- * #%L * thinkbig-feed-manager-controller * %% * Copyright (C) 2017 ThinkBig Analytics * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.fasterxml.jackson.core.type.TypeReference; import com.thinkbiganalytics.cluster.ClusterService; import com.thinkbiganalytics.cluster.ClusterServiceListener; import com.thinkbiganalytics.cluster.NiFiFlowCacheUpdateType; import com.thinkbiganalytics.feedmgr.rest.model.RegisteredTemplate; import com.thinkbiganalytics.feedmgr.service.MetadataService; import com.thinkbiganalytics.json.ObjectMapperSerializer; import com.thinkbiganalytics.metadata.api.MetadataAccess; import com.thinkbiganalytics.metadata.api.cluster.NiFiFlowCacheClusterUpdateItem; import com.thinkbiganalytics.metadata.jpa.cluster.NiFiFlowCacheClusterUpdateProvider; import com.thinkbiganalytics.nifi.rest.model.flow.NifiFlowConnection; import com.thinkbiganalytics.nifi.rest.model.flow.NifiFlowProcessGroup; import com.thinkbiganalytics.nifi.rest.model.flow.NifiFlowProcessor; import org.apache.nifi.web.api.dto.ConnectionDTO; import org.apache.nifi.web.api.dto.ProcessorDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; /** * Manage the Nifi flow cache when Kylo is clustered */ public class NifiFlowCacheClusterManager implements ClusterServiceListener { private static final String LAST_MODIFIED_KEY_PREFIX = "NIFI_FLOW_CACHE"; private static final Logger log = LoggerFactory.getLogger(NifiFlowCacheClusterManager.class); @Inject MetadataAccess metadataAccess; @Inject ClusterService clusterService; @Inject MetadataService metadataService; @Inject NiFiFlowCacheClusterUpdateProvider niFiFlowCacheProvider; @PostConstruct public void init() { clusterService.subscribe(this); } public NifiFlowCacheClusterUpdateMessage updateTemplate(String templateName) { NifiFlowCacheClusterUpdateMessage updateMessage = new NifiFlowCacheClusterUpdateMessage(NiFiFlowCacheUpdateType.TEMPLATE,templateName); updatedCache(updateMessage); return updateMessage; } public NifiFlowCacheClusterUpdateMessage updateConnections(Collection<ConnectionDTO> connections) { String json = ObjectMapperSerializer.serialize(connections); NifiFlowCacheClusterUpdateMessage updateMessage = new NifiFlowCacheClusterUpdateMessage(NiFiFlowCacheUpdateType.CONNECTION,json); updatedCache(updateMessage); return updateMessage; } public NifiFlowCacheClusterUpdateMessage updateProcessors(Collection<ProcessorDTO> processors) { //strip for serialization ... create new NifiFlowCacheSimpleProcessorDTO List<NifiFlowCacheSimpleProcessorDTO> processorsToCache =processors.stream() .map(p -> new NifiFlowCacheSimpleProcessorDTO(p.getId(),p.getName(),p.getType(),p.getParentGroupId())) .collect(Collectors.toList()); String json = ObjectMapperSerializer.serialize(processorsToCache); NifiFlowCacheClusterUpdateMessage updateMessage = new NifiFlowCacheClusterUpdateMessage(NiFiFlowCacheUpdateType.PROCESSOR, json); updatedCache(updateMessage); return updateMessage; } public NifiFlowCacheClusterUpdateMessage updateFeed(String feedName, boolean isStream,NifiFlowProcessGroup feedFlow) { NifiFlowCacheFeedUpdate feedUpdate = new NifiFlowCacheFeedUpdate(feedName,isStream, feedFlow.getId(), feedFlow.getProcessorMap().values(), feedFlow.getConnectionIdMap().values()); String json = ObjectMapperSerializer.serialize(feedUpdate); NifiFlowCacheClusterUpdateMessage updateMessage = new NifiFlowCacheClusterUpdateMessage(NiFiFlowCacheUpdateType.FEED,json); updatedCache(updateMessage); return updateMessage; } public NifiFlowCacheClusterUpdateMessage updateFeed(String feedName, boolean isStream, String feedProcessGroupId,Collection<NifiFlowProcessor> processors, Collection<NifiFlowConnection> connections) { NifiFlowCacheSimpleFeedUpdate feedUpdate = new NifiFlowCacheSimpleFeedUpdate(feedName, isStream, feedProcessGroupId, transformNifiFlowProcesors(processors), connections); String json = ObjectMapperSerializer.serialize(feedUpdate); NifiFlowCacheClusterUpdateMessage updateMessage = new NifiFlowCacheClusterUpdateMessage(NiFiFlowCacheUpdateType.FEED,json); updatedCache(updateMessage); return updateMessage; } public NifiFlowCacheFeedUpdate getFeedUpdate(String json){ NifiFlowCacheFeedUpdate update = ObjectMapperSerializer.deserialize(json,NifiFlowCacheFeedUpdate.class); return update; } public Collection<ProcessorDTO> getProcessorsUpdate(String json){ Set<ProcessorDTO> processors = ObjectMapperSerializer.deserialize(json,new TypeReference<Set<ProcessorDTO>>(){}); return processors; } public Collection<ConnectionDTO> getConnectionsUpdate(String json){ Set<ConnectionDTO> connections = ObjectMapperSerializer.deserialize(json,new TypeReference<Set<ConnectionDTO>>(){}); return connections; } public RegisteredTemplate getTemplate(String templateName){ return metadataService.findRegisteredTemplateByName(templateName); } public boolean isClustered(){ return clusterService.isClustered(); } private void updatedCache(NifiFlowCacheClusterUpdateMessage update) { metadataAccess.commit(() -> { niFiFlowCacheProvider.updatedCache(update.getType(),update.getMessage()); },MetadataAccess.SERVICE); //send it off to notify others its been updated? } public boolean needsUpdate(){ return metadataAccess.commit(() -> { return niFiFlowCacheProvider.needsUpdate(); },MetadataAccess.SERVICE); } public List<NifiFlowCacheClusterUpdateMessage> findUpdates(){ return metadataAccess.commit(() -> { List<NiFiFlowCacheClusterUpdateItem> updates = niFiFlowCacheProvider.findUpdates(); return transformUpdates(updates); },MetadataAccess.SERVICE); } public void appliedUpdates(List<NifiFlowCacheClusterUpdateMessage> updateMessages){ metadataAccess.commit(() -> { List<String> updateKeys = updateMessages.stream().map(m -> m.getUpdateKey()).collect(Collectors.toList()); niFiFlowCacheProvider.appliedUpdates(updateKeys); },MetadataAccess.SERVICE); } private List<NifiFlowCacheClusterUpdateMessage> transformUpdates(List<NiFiFlowCacheClusterUpdateItem> updates) { if(updates != null){ return updates.stream().map(update -> new NifiFlowCacheClusterUpdateMessage(update.getUpdateType(),update.getUpdateValue(), update.getUpdateKey())).collect(Collectors.toList()); } else { return Collections.emptyList(); } } private Collection<NifiFlowCacheClusterNifiFlowProcessor> transformNifiFlowProcesors(Collection<NifiFlowProcessor> processors){ if(processors != null){ return processors.stream().map(p -> new NifiFlowCacheClusterNifiFlowProcessor(p.getId(),p.getName(),p.getType(),p.getFlowId())).collect(Collectors.toList()); } else { return Collections.emptyList(); } } @Override public void onClusterMembershipChanged(List<String> previousMembers, List<String> currentMembers) { } @Override public void onConnected(List<String> currentMembers) { log.info("Kylo Cluster Node connected {} members exist. {} ",currentMembers.size(),currentMembers); //on connected reset the previous db entries if (currentMembers.size() == 1) { try { metadataAccess.commit(() -> { log.info("This is the First Member connecting to the cluster. Resetting the previous Cluster cache updates {} members exist. {} ", currentMembers.size(), currentMembers); niFiFlowCacheProvider.resetClusterSyncUpdates(); }, MetadataAccess.SERVICE); }catch (Exception e){ //log the error and carry on log.error("Error attempting to reset the NiFi Flow Cache in the database when starting the Kylo Cluster. {} ",e.getMessage(),e); } } } @Override public void onDisconnected(List<String> currentMembers) { } @Override public void onClosed(List<String> currentMembers) { } }