All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.controller.blueprint.ext.DataStoreAppConfigDefaultXMLReader Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016 Brocade Communications Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.controller.blueprint.ext;

import com.google.common.base.Strings;
import com.google.common.io.Resources;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Optional;
import javax.xml.stream.XMLStreamException;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
import org.opendaylight.yangtools.binding.DataObject;
import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.yangtools.util.xml.UntrustedXML;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaTreeInference;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * DataObject XML file reader used by {@link DataStoreAppConfigMetadata}.
 * Available as a standalone class to make it easy to write unit tests which can
 * catch malformed default "clustered-app-conf" config data XML files in
 * downstream projects.
 *
 * @author Thomas Pantelis (originally; re-factored by Michael Vorburger.ch)
 */
public class DataStoreAppConfigDefaultXMLReader {

    private static final Logger LOG = LoggerFactory.getLogger(DataStoreAppConfigDefaultXMLReader.class);

    private final String logName;
    private final String defaultAppConfigFileName;
    private final DOMSchemaService schemaService;
    private final BindingNormalizedNodeSerializer bindingSerializer;
    private final BindingContext bindingContext;
    private final ConfigURLProvider inputStreamProvider;

    @FunctionalInterface
    public interface FallbackConfigProvider {
        NormalizedNode get(SchemaTreeInference dataSchema)
            throws IOException, XMLStreamException, SAXException, URISyntaxException;
    }

    @FunctionalInterface
    public interface ConfigURLProvider {
        Optional getURL(String appConfigFileName) throws IOException;
    }

    public DataStoreAppConfigDefaultXMLReader(
            final String logName,
            final String defaultAppConfigFileName,
            final DOMSchemaService schemaService,
            final BindingNormalizedNodeSerializer bindingSerializer,
            final BindingContext bindingContext,
            final ConfigURLProvider inputStreamProvider) {

        this.logName = logName;
        this.defaultAppConfigFileName = defaultAppConfigFileName;
        this.schemaService = schemaService;
        this.bindingSerializer = bindingSerializer;
        this.bindingContext = bindingContext;
        this.inputStreamProvider = inputStreamProvider;
    }

    public DataStoreAppConfigDefaultXMLReader(
            final Class testClass,
            final String defaultAppConfigFileName,
            final DOMSchemaService schemaService,
            final BindingNormalizedNodeSerializer bindingSerializer,
            final Class klass) {
        this(testClass.getName(), defaultAppConfigFileName, schemaService, bindingSerializer,
            BindingContext.create(testClass.getName(), klass, null),
            appConfigFileName -> Optional.of(getURL(testClass, defaultAppConfigFileName)));
    }

    private static URL getURL(final Class testClass, final String defaultAppConfigFileName) {
        return Resources.getResource(testClass, defaultAppConfigFileName);
    }

    public T createDefaultInstance() throws ConfigXMLReaderException, XMLStreamException, IOException, SAXException,
            URISyntaxException {
        return createDefaultInstance(dataSchema -> {
            throw new IllegalArgumentException(
                "Failed to read XML (not creating model from defaults as runtime would, for better clarity in tests)");
        });
    }

    @SuppressWarnings("unchecked")
    public T createDefaultInstance(final FallbackConfigProvider fallback) throws ConfigXMLReaderException,
            URISyntaxException, XMLStreamException, SAXException, IOException {
        YangInstanceIdentifier yangPath = bindingSerializer.toYangInstanceIdentifier(bindingContext.appConfigPath);

        LOG.debug("{}: Creating app config instance from path {}, Qname: {}", logName, yangPath,
                bindingContext.bindingQName);

        checkNotNull(schemaService, "%s: Could not obtain the SchemaService OSGi service", logName);

        EffectiveModelContext schemaContext = schemaService.getGlobalContext();

        Module module = schemaContext.findModule(bindingContext.bindingQName.getModule()).orElse(null);
        checkNotNull(module, "%s: Could not obtain the module schema for namespace %s, revision %s",
                logName, bindingContext.bindingQName.getNamespace(), bindingContext.bindingQName.getRevision());

        final SchemaInferenceStack schemaStack = SchemaInferenceStack.of(schemaContext);
        final SchemaTreeEffectiveStatement dataSchema;
        try {
            dataSchema = schemaStack.enterSchemaTree(bindingContext.bindingQName);
        } catch (IllegalArgumentException e) {
            throw new ConfigXMLReaderException(
                logName + ": Could not obtain the schema for " + bindingContext.bindingQName, e);
        }

        checkCondition(bindingContext.schemaType.isInstance(dataSchema),
                "%s: Expected schema type %s for %s but actual type is %s", logName,
                bindingContext.schemaType, bindingContext.bindingQName, dataSchema.getClass());

        NormalizedNode dataNode = parsePossibleDefaultAppConfigXMLFile(schemaStack);
        if (dataNode == null) {
            dataNode = fallback.get(schemaStack.toSchemaTreeInference());
        }

        DataObject appConfig = bindingSerializer.fromNormalizedNode(yangPath, dataNode).getValue();

        // This shouldn't happen but need to handle it in case...
        checkNotNull(appConfig, "%s: Could not create instance for app config binding %s", logName,
                bindingContext.appConfigBindingClass);

        return (T) appConfig;
    }

    private static void checkNotNull(final Object reference, final String errorMessageFormat,
            final Object... formatArgs) throws ConfigXMLReaderException {
        checkCondition(reference != null, errorMessageFormat, formatArgs);
    }

    private static void checkCondition(final boolean expression, final String errorMessageFormat,
            final Object... formatArgs) throws ConfigXMLReaderException {
        if (!expression) {
            throw new ConfigXMLReaderException(String.format(errorMessageFormat, formatArgs));
        }
    }

    private NormalizedNode parsePossibleDefaultAppConfigXMLFile(final SchemaInferenceStack schemaStack)
            throws ConfigXMLReaderException {
        String appConfigFileName = defaultAppConfigFileName;
        if (Strings.isNullOrEmpty(appConfigFileName)) {
            String moduleName = schemaStack.currentModule().argument().getLocalName();

            appConfigFileName = moduleName + "_" + bindingContext.bindingQName.getLocalName() + ".xml";
        }

        Optional optionalURL;
        try {
            optionalURL = inputStreamProvider.getURL(appConfigFileName);
        } catch (final IOException e) {
            String msg = String.format("%s: Could not getURL()", logName);
            LOG.error(msg, e);
            throw new ConfigXMLReaderException(msg, e);
        }
        if (!optionalURL.isPresent()) {
            return null;
        }
        URL url = optionalURL.orElseThrow();
        try (InputStream is = url.openStream()) {
            Document root = UntrustedXML.newDocumentBuilder().parse(is);
            NormalizedNode dataNode = bindingContext.parseDataElement(root.getDocumentElement(),
                schemaStack.toSchemaTreeInference());

            LOG.debug("{}: Parsed data node: {}", logName, dataNode);

            return dataNode;
        } catch (final IOException | SAXException | XMLStreamException | URISyntaxException e) {
            String msg = String.format("%s: Could not read/parse app config %s", logName, url);
            LOG.error(msg, e);
            throw new ConfigXMLReaderException(msg, e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy