org.codice.ddf.registry.federationadmin.service.impl.IdentityNodeInitialization.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.registry.federationadmin.service.impl.IdentityNodeInitialization.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.service.impl;

import static org.codice.ddf.registry.schemabindings.EbrimConstants.RIM_FACTORY;

import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.data.types.Core;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.catalog.transform.InputTransformer;
import java.io.File;
import java.io.IOException;
import java.security.PrivilegedActionException;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ExtrinsicObjectType;
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.VersionInfoType;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.utils.properties.Properties;
import org.codice.ddf.configuration.SystemBaseUrl;
import org.codice.ddf.configuration.SystemInfo;
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.federationadmin.service.internal.FederationAdminException;
import org.codice.ddf.registry.federationadmin.service.internal.FederationAdminService;
import org.codice.ddf.registry.schemabindings.helper.InternationalStringTypeHelper;
import org.codice.ddf.registry.schemabindings.helper.MetacardMarshaller;
import org.codice.ddf.registry.schemabindings.helper.RegistryPackageTypeHelper;
import org.codice.ddf.registry.schemabindings.helper.SlotTypeHelper;
import org.codice.ddf.security.common.Security;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Creates a registry identity node when DDF Registry is first instantiated. If a previous registry
 * node is not already found, then a registry identity metacard and its attributes are created.
 * FederationAdminService adds the newly created identity metacard as a registry entry.
 */
public class IdentityNodeInitialization {

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

    private static final String UNKNOWN_SITE_NAME = "Unknown Site Name";

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

    private static final long DEFAULT_RETRY_INTERVAL_SECONDS = 30;

    private static final int SHUTDOWN_TIMEOUT_SECONDS = 60;

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

    private static final String SYSTEM_PROPERTIES_FILE = "custom.system.properties";

    private FederationAdminService federationAdminService;

    private InputTransformer registryTransformer;

    private MetacardMarshaller metacardMarshaller;

    private SlotTypeHelper slotTypeHelper = new SlotTypeHelper();

    private InternationalStringTypeHelper internationalStringTypeHelper = new InternationalStringTypeHelper();

    private long retryInterval = DEFAULT_RETRY_INTERVAL_SECONDS;

    private final ScheduledExecutorService executor;

    public IdentityNodeInitialization(ScheduledExecutorService executor) {
        this(executor, DEFAULT_RETRY_INTERVAL_SECONDS);
    }

    public IdentityNodeInitialization(ScheduledExecutorService executor, long retryInterval) {
        this.retryInterval = retryInterval;
        this.executor = executor;
    }

    public void init() {
        // will keep checking for changes and shouldn't hold up anything
        executor.scheduleAtFixedRate(this::updateOrCreateIdentity, 1, retryInterval, TimeUnit.SECONDS);
    }

    public void destroy() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
                executor.shutdownNow();
                if (!executor.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
                    LOGGER.error(
                            "Identity node initialization thread didn't terminate. This thread will continue until a system restart.");
                }
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    boolean updateOrCreateIdentity() {
        try {
            Security security = Security.getInstance();

            security.runAsAdminWithException(() -> {
                Optional<Metacard> optional = federationAdminService.getLocalRegistryIdentityMetacard();
                if (optional.isPresent()) {
                    Metacard metacard = optional.get();
                    if (!metacard.getTitle().equals(SystemInfo.getSiteName())) {
                        updateIdentityNodeName(metacard);
                    }
                }
                if (!optional.isPresent()) {
                    createIdentityNode();
                }
                return null;
            });
        } catch (PrivilegedActionException e) {
            LOGGER.debug("Error checking for local registry identity node. Will try again later");
            return false;
        }
        return true;
    }

    private void updateIdentityNodeName(Metacard metacard) throws FederationAdminException, ParserException {
        RegistryPackageType registryPackage = metacardMarshaller.getRegistryPackageFromMetacard(metacard);

        String siteName = SystemInfo.getSiteName();
        RegistryPackageTypeHelper registryPackageTypeHelper = new RegistryPackageTypeHelper();

        registryPackageTypeHelper.getExtrinsicObjects(registryPackage.getRegistryObjectList()).stream()
                .filter(extrinsicObjectType -> extrinsicObjectType.getObjectType()
                        .equals(RegistryConstants.REGISTRY_NODE_OBJECT_TYPE))
                .findFirst().ifPresent(extrinsicObjectType -> extrinsicObjectType
                        .setName(internationalStringTypeHelper.create(siteName)));
        Metacard identityMetacard = getRegistryMetacardFromRegistryPackage(registryPackage);
        if (identityMetacard != null) {
            copyTransientAttributes(identityMetacard, metacard);
            federationAdminService.updateRegistryEntry(identityMetacard);
        }
    }

    private void copyTransientAttributes(Metacard updateMetacard, Metacard existingMetacard) {
        RegistryObjectMetacardType.TRANSIENT_ATTRIBUTES.stream().filter(
                key -> updateMetacard.getAttribute(key) == null && existingMetacard.getAttribute(key) != null)
                .forEach(key -> updateMetacard.setAttribute(existingMetacard.getAttribute(key)));
        // Set the id to protect against a timing issue sometimes seen with framework plugins
        updateMetacard.setAttribute(existingMetacard.getAttribute(Core.ID));
    }

    private void createIdentityNode() throws FederationAdminException {
        String registryPackageId = System.getProperty(RegistryConstants.REGISTRY_ID_PROPERTY);
        if (StringUtils.isEmpty(registryPackageId)) {
            registryPackageId = RegistryConstants.GUID_PREFIX + UUID.randomUUID().toString().replaceAll("-", "");
            setSystemRegistryId(registryPackageId);
        }
        LOGGER.info("Creating registry identity node: {} {}", SystemInfo.getSiteName(), registryPackageId);
        RegistryPackageType registryPackage = RIM_FACTORY.createRegistryPackageType();
        registryPackage.setId(registryPackageId);
        registryPackage.setObjectType(RegistryConstants.REGISTRY_NODE_OBJECT_TYPE);

        ExtrinsicObjectType extrinsicObject = RIM_FACTORY.createExtrinsicObjectType();
        extrinsicObject.setObjectType(RegistryConstants.REGISTRY_NODE_OBJECT_TYPE);

        String extrinsicObjectId = RegistryConstants.GUID_PREFIX + UUID.randomUUID().toString().replaceAll("-", "");
        extrinsicObject.setId(extrinsicObjectId);

        String siteName = SystemInfo.getSiteName();
        if (StringUtils.isNotBlank(siteName)) {
            extrinsicObject.setName(internationalStringTypeHelper.create(siteName));
        } else {
            extrinsicObject.setName(internationalStringTypeHelper.create(UNKNOWN_SITE_NAME));
        }

        String home = SystemBaseUrl.EXTERNAL.getBaseUrl();
        extrinsicObject.setHome(home);

        String version = SystemInfo.getVersion();
        if (StringUtils.isNotBlank(version)) {
            VersionInfoType versionInfo = RIM_FACTORY.createVersionInfoType();
            versionInfo.setVersionName(version);

            extrinsicObject.setVersionInfo(versionInfo);
        }

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

        SlotType1 lastUpdated = slotTypeHelper.create(RegistryConstants.XML_LAST_UPDATED_NAME, rightNow, DATE_TIME);
        extrinsicObject.getSlot().add(lastUpdated);

        SlotType1 liveDate = slotTypeHelper.create(RegistryConstants.XML_LIVE_DATE_NAME, rightNow, DATE_TIME);
        extrinsicObject.getSlot().add(liveDate);

        if (registryPackage.getRegistryObjectList() == null) {
            registryPackage.setRegistryObjectList(RIM_FACTORY.createRegistryObjectListType());
        }

        registryPackage.getRegistryObjectList().getIdentifiable()
                .add(RIM_FACTORY.createIdentifiable(extrinsicObject));

        Metacard identityMetacard = getRegistryMetacardFromRegistryPackage(registryPackage);
        if (identityMetacard != null) {
            identityMetacard
                    .setAttribute(new AttributeImpl(RegistryObjectMetacardType.REGISTRY_IDENTITY_NODE, true));
            identityMetacard.setAttribute(new AttributeImpl(RegistryObjectMetacardType.REGISTRY_LOCAL_NODE, true));
            federationAdminService.addRegistryEntry(identityMetacard);
        }
    }

    private void setSystemRegistryId(String regId) {
        String etcDir = System.getProperty(KARAF_ETC);
        String systemPropertyFilename = etcDir + File.separator + SYSTEM_PROPERTIES_FILE;

        File systemPropertiesFile = new File(systemPropertyFilename);
        try {
            Properties systemProperties = new Properties(systemPropertiesFile);
            systemProperties.put(RegistryConstants.REGISTRY_ID_PROPERTY, regId);
            System.setProperty(RegistryConstants.REGISTRY_ID_PROPERTY, regId);
            systemProperties.save();
        } catch (IOException e) {
            LOGGER.warn("Exception while writing registry-id to system.properties file.", e);
        }
    }

    private Metacard getRegistryMetacardFromRegistryPackage(RegistryPackageType registryPackage)
            throws FederationAdminException {
        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;
    }

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

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

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