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) {
}
}