org.codice.ddf.registry.federationadmin.impl.FederationAdmin.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.registry.federationadmin.impl.FederationAdmin.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.registry.federationadmin.impl;

import com.google.gson.Gson;
import ddf.action.MultiActionProvider;
import ddf.catalog.data.Attribute;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.endpoint.CatalogEndpoint;
import ddf.catalog.service.ConfiguredService;
import ddf.catalog.source.Source;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.catalog.transform.InputTransformer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import javax.xml.bind.JAXBElement;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ExtrinsicObjectType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.RegistryObjectType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.RegistryPackageType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.SlotType1;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ValueListType;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.codice.ddf.admin.core.api.ConfigurationDetails;
import org.codice.ddf.admin.core.api.ConfigurationProperties;
import org.codice.ddf.admin.core.api.Service;
import org.codice.ddf.configuration.SystemBaseUrl;
import org.codice.ddf.parser.ParserException;
import org.codice.ddf.registry.common.RegistryConstants;
import org.codice.ddf.registry.common.metacard.RegistryObjectMetacardType;
import org.codice.ddf.registry.common.metacard.RegistryUtility;
import org.codice.ddf.registry.federationadmin.internal.FederationAdminMBean;
import org.codice.ddf.registry.federationadmin.service.internal.FederationAdminException;
import org.codice.ddf.registry.federationadmin.service.internal.FederationAdminService;
import org.codice.ddf.registry.federationadmin.service.internal.RegistrySourceConfiguration;
import org.codice.ddf.registry.schemabindings.EbrimConstants;
import org.codice.ddf.registry.schemabindings.converter.type.RegistryPackageTypeConverter;
import org.codice.ddf.registry.schemabindings.converter.web.RegistryPackageWebConverter;
import org.codice.ddf.registry.schemabindings.helper.MetacardMarshaller;
import org.codice.ddf.registry.schemabindings.helper.SlotTypeHelper;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswConstants;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FederationAdmin implements FederationAdminMBean, EventHandler {

    public static final String SUMMARY_METACARD_ID = "metacardId";

    public static final String SUMMARY_REGISTRY_ID = "registryId";

    public static final String SUMMARY_NAME = "name";

    public static final String SUMMARY_LOCAL_NODE = "localNode";

    public static final String SUMMARY_IDENTITY_NODE = "identityNode";

    public static final String SUMMARY_REPORT_ACTION = "reportAction";

    public static final String SUMMARY_FILTERED = "filtered";

    public static final String FILTER_INVERTED = "filterInverted";

    public static final String CLIENT_MODE = "clientMode";

    private static final Logger LOGGER = LoggerFactory.getLogger(FederationAdmin.class);

    private static final String DISABLED = "_disabled";

    private static final String TRANSIENT_VALUES_KEY = "TransientValues";

    private static final String AUTO_POPULATE_VALUES_KEY = "autoPopulateValues";

    private static final String SERVICE_BINDINGS_KEY = "ServiceBinding";

    private static final String CUSTOM_SLOTS_KEY = "customSlots";

    private static final String NODES_KEY = "nodes";

    private static final String KARAF_ETC = "karaf.etc";

    private static final String REGISTRY_CONFIG_DIR = "registry";

    private static final String REGISTRY_FIELDS_FILE = "registry-custom-slots.json";

    private static final String DATE_TIME = CswConstants.XML_SCHEMA_NAMESPACE_PREFIX.concat(":dateTime");

    private static final String REPORT_ACTION_PROVIDER_ID = "catalog.data.metacard.registry";

    private static final String METACARD_PROPERTY = "ddf.catalog.event.metacard";

    private static final String DELETED_TOPIC = "ddf/catalog/event/DELETED";

    private final AdminHelper helper;

    private MBeanServer mbeanServer;

    private ObjectName objectName;

    private FederationAdminService federationAdminService;

    private InputTransformer registryTransformer;

    private MetacardMarshaller metacardMarshaller;

    private Map<String, Object> customSlots;

    private Map<String, Map<String, String>> endpointMap = new HashMap<>();

    private RegistryPackageTypeConverter registryTypeConverter;

    private RegistryPackageWebConverter registryMapConverter;

    private SlotTypeHelper slotHelper;

    private RegistrySourceConfiguration sourceConfigRefresh;

    private MultiActionProvider registryActionProvider;

    private Map<String, Map<String, Object>> summaryCache = new ConcurrentHashMap<>();

    private boolean cacheInitialized = false;

    private Map<String, Consumer> filterPropertySetterMap;

    public FederationAdmin(AdminHelper helper) {
        configureMBean();
        this.helper = helper;

        filterPropertySetterMap = new HashMap<>();
        filterPropertySetterMap.put(CLIENT_MODE, e -> helper.setClientMode((boolean) e));
        filterPropertySetterMap.put(FILTER_INVERTED, e -> helper.setFilterInverted((boolean) e));
        filterPropertySetterMap.put(SUMMARY_FILTERED, e -> helper.setFilteredNodeList((List<String>) e));
    }

    @Override
    public void handleEvent(Event event) {
        Metacard mcard = (Metacard) event.getProperty(METACARD_PROPERTY);
        if (mcard == null || !RegistryUtility.isRegistryMetacard(mcard)) {
            return;
        }
        String registryId = RegistryUtility.getRegistryId(mcard);
        if (event.getTopic().equals(DELETED_TOPIC)) {
            summaryCache.remove(registryId);
        } else {
            summaryCache.put(registryId, getSummaryMap(mcard));
        }
    }

    @Override
    public String createLocalEntry(Map<String, Object> registryMap) throws FederationAdminException {

        Optional<RegistryPackageType> registryPackageOptional = registryTypeConverter.convert(registryMap);
        RegistryPackageType registryPackage = registryPackageOptional
                .orElseThrow(() -> new FederationAdminException(
                        "Error creating local registry entry. Couldn't convert registry map to a registry package."));

        if (!registryPackage.isSetHome()) {
            registryPackage.setHome(SystemBaseUrl.EXTERNAL.getBaseUrl());
        }

        if (!registryPackage.isSetObjectType()) {
            registryPackage.setObjectType(RegistryConstants.REGISTRY_NODE_OBJECT_TYPE);
        }

        if (!registryPackage.isSetId()) {
            String registryPackageId = RegistryConstants.GUID_PREFIX
                    + UUID.randomUUID().toString().replaceAll("-", "");
            registryPackage.setId(registryPackageId);
        }

        updateDateFields(registryPackage);

        Metacard metacard = getRegistryMetacardFromRegistryPackage(registryPackage);
        metacard.setAttribute(new AttributeImpl(RegistryObjectMetacardType.REGISTRY_LOCAL_NODE, true));
        return federationAdminService.addRegistryEntry(metacard);
    }

    @Override
    public String createLocalEntry(String base64EncodedXmlData) throws FederationAdminException {
        if (StringUtils.isBlank(base64EncodedXmlData)) {
            throw new FederationAdminException("Error creating local entry. String provided was blank.");
        }

        String metacardId;
        try (InputStream xmlStream = new ByteArrayInputStream(Base64.getDecoder().decode(base64EncodedXmlData))) {
            Metacard metacard = getRegistryMetacardFromInputStream(xmlStream);
            metacard.setAttribute(new AttributeImpl(RegistryObjectMetacardType.REGISTRY_LOCAL_NODE, true));
            metacardId = federationAdminService.addRegistryEntry(metacard);
        } catch (IOException | IllegalArgumentException e) {
            throw new FederationAdminException("Error creating local entry. Couldn't decode string.", e);
        }
        return metacardId;
    }

    @Override
    public void updateLocalEntry(Map<String, Object> registryObjectMap) throws FederationAdminException {

        Optional<RegistryPackageType> registryPackageOptional = registryTypeConverter.convert(registryObjectMap);
        RegistryPackageType registryPackage = registryPackageOptional
                .orElseThrow(() -> new FederationAdminException(
                        "Error updating local registry entry. Couldn't convert registry map to a registry package."));

        updateDateFields(registryPackage);

        List<Metacard> existingMetacards = federationAdminService
                .getLocalRegistryMetacardsByRegistryIds(Collections.singletonList(registryPackage.getId()));

        if (CollectionUtils.isEmpty(existingMetacards)) {
            String message = "Error updating local registry entry. Registry metacard not found.";
            LOGGER.debug("{} Registry ID: {}", message, registryPackage.getId());
            throw new FederationAdminException(message);
        }

        if (existingMetacards.size() > 1) {
            throw new FederationAdminException(
                    "Error updating local registry entry. Multiple registry metacards found.");
        }
        Metacard existingMetacard = existingMetacards.get(0);

        Metacard updateMetacard = getRegistryMetacardFromRegistryPackage(registryPackage);
        updateMetacard.setAttribute(new AttributeImpl(Metacard.ID, existingMetacard.getId()));

        federationAdminService.updateRegistryEntry(updateMetacard);
    }

    @Override
    public void deleteLocalEntry(List<String> ids) throws FederationAdminException {
        if (CollectionUtils.isEmpty(ids)) {
            throw new FederationAdminException("Error deleting local registry entries. No ids provided.");
        }

        List<Metacard> localMetacards = federationAdminService.getRegistryMetacardsByRegistryIds(ids, true);

        List<String> metacardIds = new ArrayList<>();

        metacardIds.addAll(localMetacards.stream().map(Metacard::getId).collect(Collectors.toList()));
        if (ids.size() > metacardIds.size()) {
            String message = "Error deleting local registry entries. ";
            LOGGER.debug("{} Registry Ids provided: {}. Registry metacard ids found: {}", message, ids,
                    metacardIds);
            throw new FederationAdminException(message);
        }

        federationAdminService.deleteRegistryEntriesByMetacardIds(metacardIds);
    }

    @Override
    public Map<String, Object> getLocalNodes() throws FederationAdminException {
        Map<String, Object> localNodes = new HashMap<>();
        List<Map<String, Object>> registryWebMaps;

        List<RegistryPackageType> registryPackages = federationAdminService.getLocalRegistryObjects();

        List<Metacard> metacards = federationAdminService.getLocalRegistryMetacards();
        Map<String, Metacard> metacardByRegistryIdMap = getRegistryIdMetacardMap(metacards);

        registryWebMaps = getWebMapsFromRegistryPackages(registryPackages, metacardByRegistryIdMap);

        if (customSlots != null) {
            localNodes.put(CUSTOM_SLOTS_KEY, customSlots);
        }

        Map<String, Object> autoPopulateMap = new HashMap<>();
        autoPopulateMap.put(SERVICE_BINDINGS_KEY, endpointMap.values());
        localNodes.put(AUTO_POPULATE_VALUES_KEY, autoPopulateMap);

        localNodes.put(NODES_KEY, registryWebMaps);

        return localNodes;
    }

    @Override
    public boolean registryStatus(String servicePID) {
        try {
            List<Source> sources = helper.getRegistrySources();
            for (Source source : sources) {
                if (source instanceof ConfiguredService) {
                    ConfiguredService cs = (ConfiguredService) source;
                    try {
                        Configuration config = helper.getConfiguration(cs);
                        if (config != null
                                && config.getProperties().get(Constants.SERVICE_PID).equals(servicePID)) {
                            try {
                                return source.isAvailable();
                            } catch (Exception e) {
                                LOGGER.info("Couldn't get availability on registry {}:", servicePID, e);
                            }
                        }
                    } catch (IOException e) {
                        LOGGER.info("Couldn't find configuration for source '{}'", source.getId());
                    }
                } else {
                    LOGGER.info("Source '{}' not a configured service", source.getId());
                }
            }
        } catch (InvalidSyntaxException e) {
            LOGGER.info("Could not get service reference list");
        }

        return false;
    }

    @Override
    public List<Service> allRegistryInfo() {

        List<Service> metatypes = helper.getMetatypes();

        for (Service metatype : metatypes) {
            try {
                List<Configuration> configs = helper.getConfigurations(metatype);

                List<ConfigurationDetails> configurations = new ArrayList<>();
                if (configs != null) {
                    for (Configuration config : configs) {
                        ConfigurationDetails registry = new ConfigurationDetailsImpl();

                        boolean disabled = config.getPid().endsWith(DISABLED);
                        registry.setId(config.getPid());
                        registry.setEnabled(!disabled);
                        registry.setFactoryPid(config.getFactoryPid());

                        if (!disabled) {
                            registry.setName(helper.getName(config));
                            registry.setBundleName(helper.getBundleName(config));
                            registry.setBundleLocation(config.getBundleLocation());
                            registry.setBundle(helper.getBundleId(config));
                        } else {
                            registry.setName(config.getPid());
                        }

                        Dictionary<String, Object> properties = config.getProperties();
                        ConfigurationProperties plist = new ConfigurationPropertiesImpl();
                        for (String key : Collections.list(properties.keys())) {
                            plist.put(key, properties.get(key));
                        }
                        registry.setConfigurationProperties(plist);

                        configurations.add(registry);
                    }
                    metatype.setConfigurations(configurations);
                }
            } catch (InvalidSyntaxException | IOException e) {
                LOGGER.info("Error getting registry info:", e);
            }
        }

        Collections.sort(metatypes, (o1, o2) -> ((String) o1.get("id")).compareToIgnoreCase((String) o2.get("id")));
        return metatypes;
    }

    @Override
    public Map<String, Object> allRegistryMetacards() {
        Map<String, Object> nodes = new HashMap<>();
        List<Map<String, Object>> registryMetacardInfo = new ArrayList<>();

        try {
            List<RegistryPackageType> registryMetacardObjects = federationAdminService.getRegistryObjects();

            List<Metacard> metacards = federationAdminService.getRegistryMetacards();
            Map<String, Metacard> metacardByRegistryIdMap = getRegistryIdMetacardMap(metacards);

            registryMetacardInfo = getWebMapsFromRegistryPackages(registryMetacardObjects, metacardByRegistryIdMap);

        } catch (FederationAdminException e) {
            LOGGER.info("Couldn't get remote registry metacards ", e);
        }

        if (customSlots != null) {
            nodes.put(CUSTOM_SLOTS_KEY, customSlots);
        }

        Map<String, Object> autoPopulateMap = new HashMap<>();
        autoPopulateMap.put(SERVICE_BINDINGS_KEY, endpointMap.values());
        nodes.put(AUTO_POPULATE_VALUES_KEY, autoPopulateMap);

        nodes.put(NODES_KEY, registryMetacardInfo);

        return nodes;
    }

    @Override
    public Map<String, Object> registryMetacard(String registryId) {
        if (!RegistryUtility.validRegistryId(registryId)) {
            throw new IllegalArgumentException(
                    "Passed in registryId is invalid. Must conform to " + RegistryUtility.REGISTRY_ID_REGEX);
        }
        Map<String, Object> nodes = new HashMap<>();
        List<Map<String, Object>> registryMetacardInfo = new ArrayList<>();
        try {
            List<RegistryPackageType> registryMetacardObjects = Collections
                    .singletonList(federationAdminService.getRegistryObjectByRegistryId(registryId));

            List<Metacard> metacards = federationAdminService
                    .getRegistryMetacardsByRegistryIds(Collections.singletonList(registryId));
            Map<String, Metacard> metacardByRegistryIdMap = getRegistryIdMetacardMap(metacards);

            registryMetacardInfo = getWebMapsFromRegistryPackages(registryMetacardObjects, metacardByRegistryIdMap);

        } catch (FederationAdminException e) {
            LOGGER.info("Couldn't get registry metacards ", e);
        }

        nodes.put(NODES_KEY, registryMetacardInfo);

        return nodes;
    }

    @Override
    public Map<String, Object> allRegistryMetacardsSummary() {
        Map<String, Object> nodes = new HashMap<>();
        if (!cacheInitialized) {
            try {
                federationAdminService.getRegistryMetacards().stream().forEach(metacard -> summaryCache
                        .put(RegistryUtility.getRegistryId(metacard), getSummaryMap(metacard)));
                cacheInitialized = true;
            } catch (FederationAdminException e) {
                LOGGER.info("Couldn't get remote registry metacards ", e);
            }
        }

        if (customSlots != null) {
            nodes.put(CUSTOM_SLOTS_KEY, customSlots);
        }

        Map<String, Object> autoPopulateMap = new HashMap<>();
        autoPopulateMap.put(SERVICE_BINDINGS_KEY, endpointMap.values());
        nodes.put(AUTO_POPULATE_VALUES_KEY, autoPopulateMap);

        try {
            Map<String, Object> filterProps = helper.getFilterProperties();
            List<String> filterList = Arrays.asList((String[]) filterProps.remove(SUMMARY_FILTERED));
            for (Map<String, Object> entry : summaryCache.values()) {
                entry.put(SUMMARY_FILTERED, filterList.contains(entry.get(SUMMARY_REGISTRY_ID)));
            }

            nodes.putAll(filterProps);
        } catch (IOException e) {
            LOGGER.debug("Error adding filter information to metacard summary response", e);
        }

        nodes.put(NODES_KEY, new ArrayList(summaryCache.values()));
        return nodes;
    }

    private Map<String, Object> getSummaryMap(Metacard metacard) {
        Map<String, Object> metacardSummary = new HashMap<>();
        metacardSummary.put(SUMMARY_METACARD_ID, metacard.getId());
        metacardSummary.put(SUMMARY_REGISTRY_ID, RegistryUtility.getRegistryId(metacard));
        metacardSummary.put(SUMMARY_NAME, metacard.getTitle());
        metacardSummary.put(Metacard.CREATED, metacard.getCreatedDate());
        metacardSummary.put(Metacard.MODIFIED, metacard.getModifiedDate());
        metacardSummary.put(SUMMARY_IDENTITY_NODE, RegistryUtility.isIdentityNode(metacard));
        metacardSummary.put(SUMMARY_LOCAL_NODE, RegistryUtility.isLocalNode(metacard));
        metacardSummary.put(SUMMARY_REPORT_ACTION, getReportAction(metacard));
        return metacardSummary;
    }

    @Override
    public void regenerateRegistrySources(List<String> ids) {
        try {
            for (String regId : ids) {
                sourceConfigRefresh.regenerateOneSource(regId);
            }
        } catch (FederationAdminException e) {
            LOGGER.debug("Error regenerating registry sources.", e);
        }
    }

    @Override
    public void nodeFilterProperties(Map<String, Object> properties) {
        properties.entrySet().stream().filter(e -> filterPropertySetterMap.containsKey(e.getKey()))
                .forEach(e -> filterPropertySetterMap.get(e.getKey()).accept(e.getValue()));
    }

    private List<Map<String, Object>> getWebMapsFromRegistryPackages(List<RegistryPackageType> packages,
            Map<String, Metacard> metacardByRegistryIdMap) {
        List<Map<String, Object>> registryMaps = new ArrayList<>();
        for (RegistryPackageType registryPackage : packages) {
            Map<String, Object> registryWebMap = registryMapConverter.convert(registryPackage);

            Metacard metacard = metacardByRegistryIdMap.get(registryPackage.getId());
            Map<String, Object> transientValues = getTransientValuesMap(metacard);
            if (MapUtils.isNotEmpty(transientValues)) {
                registryWebMap.put(TRANSIENT_VALUES_KEY, transientValues);
            }

            if (MapUtils.isNotEmpty(registryWebMap)) {
                registryMaps.add(registryWebMap);
            }
        }
        return registryMaps;
    }

    private String getReportAction(Metacard metacard) {
        String actionUrl = null;
        if (registryActionProvider != null && registryActionProvider.canHandle(metacard)) {
            actionUrl = registryActionProvider.getActions(metacard).stream()
                    .filter(e -> e.getId().equals(REPORT_ACTION_PROVIDER_ID)).findFirst()
                    .map(action -> action.getUrl().toString()).orElse(null);
        }
        return actionUrl;
    }

    private Metacard getRegistryMetacardFromRegistryPackage(RegistryPackageType registryPackage)
            throws FederationAdminException {
        if (registryPackage == null) {
            throw new FederationAdminException(
                    "Error creating metacard from registry package. Null package was received.");
        }
        Metacard metacard;

        try {
            metacard = registryTransformer
                    .transform(metacardMarshaller.getRegistryPackageAsInputStream(registryPackage));
        } catch (IOException | CatalogTransformerException | ParserException e) {
            String message = "Error creating metacard from registry package.";
            LOGGER.debug("{} Registry id: {}", message, registryPackage.getId());
            throw new FederationAdminException(message, e);
        }

        return metacard;
    }

    private Metacard getRegistryMetacardFromInputStream(InputStream inputStream) throws FederationAdminException {
        if (inputStream == null) {
            throw new FederationAdminException(
                    "Error converting input stream to a metacard. Null input stream provided.");
        }

        Metacard metacard;
        try {
            metacard = registryTransformer.transform(inputStream);
        } catch (IOException | CatalogTransformerException e) {
            throw new FederationAdminException(
                    "Error getting metacard. RegistryTransformer couldn't convert the input stream.", e);
        }

        return metacard;
    }

    private Map<String, Metacard> getRegistryIdMetacardMap(List<Metacard> metacards) {
        Map<String, Metacard> registryIdMetacardMap = new HashMap<>();

        for (Metacard metacard : metacards) {
            String registryId = RegistryUtility.getRegistryId(metacard);
            if (registryId == null) {
                continue;
            }

            registryIdMetacardMap.put(registryId, metacard);
        }

        return registryIdMetacardMap;
    }

    private Map<String, Object> getTransientValuesMap(Metacard metacard) {
        Map<String, Object> transientValuesMap = new HashMap<>();
        if (metacard != null) {
            for (String transientAttributeKey : RegistryObjectMetacardType.TRANSIENT_ATTRIBUTES) {
                Attribute transientAttribute = metacard.getAttribute(transientAttributeKey);

                if (transientAttribute != null) {
                    transientValuesMap.put(transientAttributeKey, transientAttribute.getValues());
                }
            }
        }
        return transientValuesMap;
    }

    private void updateDateFields(RegistryPackageType rpt) {

        ExtrinsicObjectType nodeInfo = null;
        for (JAXBElement identifiable : rpt.getRegistryObjectList().getIdentifiable()) {
            RegistryObjectType registryObject = (RegistryObjectType) identifiable.getValue();

            if (registryObject instanceof ExtrinsicObjectType
                    && RegistryConstants.REGISTRY_NODE_OBJECT_TYPE.equals(registryObject.getObjectType())) {
                nodeInfo = (ExtrinsicObjectType) registryObject;
                break;
            }
        }
        if (nodeInfo != null) {
            boolean liveDateFound = false;
            boolean lastUpdatedFound = false;

            OffsetDateTime now = OffsetDateTime.now(ZoneId.of(ZoneOffset.UTC.toString()));
            String rightNow = now.toString();

            for (SlotType1 slot : nodeInfo.getSlot()) {
                if (slot.getName().equals(RegistryConstants.XML_LIVE_DATE_NAME)) {
                    liveDateFound = true;
                } else if (slot.getName().equals(RegistryConstants.XML_LAST_UPDATED_NAME)) {
                    ValueListType valueList = EbrimConstants.RIM_FACTORY.createValueListType();
                    valueList.getValue().add(rightNow);
                    slot.setValueList(EbrimConstants.RIM_FACTORY.createValueList(valueList));
                    lastUpdatedFound = true;
                }
            }

            if (!liveDateFound) {
                SlotType1 liveDate = slotHelper.create(RegistryConstants.XML_LIVE_DATE_NAME, rightNow, DATE_TIME);

                nodeInfo.getSlot().add(liveDate);
            }

            if (!lastUpdatedFound) {
                SlotType1 lastUpdated = slotHelper.create(RegistryConstants.XML_LAST_UPDATED_NAME, rightNow,
                        DATE_TIME);

                nodeInfo.getSlot().add(lastUpdated);
            }
        }
    }

    private void configureMBean() {
        mbeanServer = ManagementFactory.getPlatformMBeanServer();

        try {
            objectName = new ObjectName(FederationAdminMBean.OBJECT_NAME);
        } catch (MalformedObjectNameException e) {
            LOGGER.info("Exception while creating object name: {}", FederationAdminMBean.OBJECT_NAME, e);
        }

        try {
            try {
                mbeanServer.registerMBean(new StandardMBean(this, FederationAdminMBean.class), objectName);
            } catch (InstanceAlreadyExistsException e) {
                mbeanServer.unregisterMBean(objectName);
                mbeanServer.registerMBean(new StandardMBean(this, FederationAdminMBean.class), objectName);
            }
        } catch (Exception e) {
            LOGGER.info("Could not register mbean.", e);
        }
    }

    public void destroy() throws MBeanRegistrationException {
        try {
            if (objectName != null && mbeanServer != null) {
                mbeanServer.unregisterMBean(objectName);
            }
        } catch (Exception e) {
            LOGGER.info("Exception un registering mbean: ", e);
            throw new MBeanRegistrationException(e);
        }
    }

    public void init() {

        Path path = Paths.get(System.getProperty(KARAF_ETC), REGISTRY_CONFIG_DIR, REGISTRY_FIELDS_FILE);
        if (path.toFile().exists()) {
            try {
                String registryFieldsJsonString = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
                Gson gson = new Gson();
                customSlots = new HashMap<>();
                customSlots = (Map<String, Object>) gson.fromJson(registryFieldsJsonString, customSlots.getClass());
            } catch (IOException e) {
                LOGGER.debug(
                        "Error reading {}. This will result in no custom fields being shown for registry node editing",
                        path.toString(), e);
            }
        }
    }

    public void bindEndpoint(ServiceReference reference) {
        BundleContext context = getContext();
        if (reference != null && context != null) {
            CatalogEndpoint endpoint = (CatalogEndpoint) context.getService(reference);
            Map<String, String> properties = endpoint.getEndpointProperties();
            endpointMap.put(properties.get(CatalogEndpoint.ID_KEY), properties);
        }
    }

    public void unbindEndpoint(ServiceReference reference) {
        BundleContext context = getContext();
        if (reference != null && context != null) {
            CatalogEndpoint endpoint = (CatalogEndpoint) context.getService(reference);
            Map<String, String> properties = endpoint.getEndpointProperties();
            endpointMap.remove(properties.get(CatalogEndpoint.ID_KEY));
        }
    }

    protected BundleContext getContext() {
        Bundle bundle = FrameworkUtil.getBundle(FederationAdmin.class);
        if (bundle != null) {
            return bundle.getBundleContext();
        }
        return null;
    }

    public void setFederationAdminService(FederationAdminService federationAdminService) {
        this.federationAdminService = federationAdminService;
    }

    public void setMetacardMarshaller(MetacardMarshaller metacardMarshaller) {
        this.metacardMarshaller = metacardMarshaller;
    }

    public void setRegistryTransformer(InputTransformer inputTransformer) {
        this.registryTransformer = inputTransformer;
    }

    public void setRegistryTypeConverter(RegistryPackageTypeConverter registryTypeConverter) {
        this.registryTypeConverter = registryTypeConverter;
    }

    public void setRegistryMapConverter(RegistryPackageWebConverter registryMapConverter) {
        this.registryMapConverter = registryMapConverter;
    }

    public void setSlotHelper(SlotTypeHelper slotHelper) {
        this.slotHelper = slotHelper;
    }

    public void setSourceConfigRefresh(RegistrySourceConfiguration sourceConfigRefresh) {
        this.sourceConfigRefresh = sourceConfigRefresh;
    }

    public void setRegistryActionProvider(MultiActionProvider provider) {
        this.registryActionProvider = provider;
    }

    private static class ConfigurationDetailsImpl extends HashMap<String, Object> implements ConfigurationDetails {
    }

    private static class ConfigurationPropertiesImpl extends HashMap<String, Object>
            implements ConfigurationProperties {
    }
}