org.smartdeveloperhub.vocabulary.publisher.VocabularyPublisher.java Source code

Java tutorial

Introduction

Here is the source code for org.smartdeveloperhub.vocabulary.publisher.VocabularyPublisher.java

Source

/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the Smart Developer Hub Project:
 *     http://www.smartdeveloperhub.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2015-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.smartdeveloperhub.vocabulary:sdh-vocabulary:0.3.0
 *   Bundle      : sdh-vocabulary-0.3.0.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.smartdeveloperhub.vocabulary.publisher;

import static io.undertow.Handlers.path;
import static org.smartdeveloperhub.vocabulary.publisher.handlers.MoreHandlers.contentNegotiation;
import static org.smartdeveloperhub.vocabulary.publisher.handlers.MoreHandlers.methodController;
import static org.smartdeveloperhub.vocabulary.publisher.handlers.MoreHandlers.moduleReverseProxy;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Scanner;

import org.ldp4j.http.CharacterEncodings;
import org.ldp4j.http.MediaType;
import org.ldp4j.http.MediaTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smartdeveloperhub.vocabulary.config.ConfigurationFactory;
import org.smartdeveloperhub.vocabulary.publisher.config.DocumentationConfig;
import org.smartdeveloperhub.vocabulary.publisher.config.PublisherConfig;
import org.smartdeveloperhub.vocabulary.publisher.handlers.ContentNegotiationHandler;
import org.smartdeveloperhub.vocabulary.publisher.handlers.NegotiableContent;
import org.smartdeveloperhub.vocabulary.publisher.spi.DocumentationDeployment;
import org.smartdeveloperhub.vocabulary.publisher.spi.DocumentationDeploymentFactory;
import org.smartdeveloperhub.vocabulary.publisher.spi.DocumentationProvider;
import org.smartdeveloperhub.vocabulary.publisher.spi.DocumentationProviderFactory;
import org.smartdeveloperhub.vocabulary.util.AppAssembler;
import org.smartdeveloperhub.vocabulary.util.Application;
import org.smartdeveloperhub.vocabulary.util.Catalog;
import org.smartdeveloperhub.vocabulary.util.Catalogs;
import org.smartdeveloperhub.vocabulary.util.Module;
import org.smartdeveloperhub.vocabulary.util.Module.Format;
import org.smartdeveloperhub.vocabulary.util.Result;
import org.smartdeveloperhub.vocabulary.util.SerializationManager;

import com.google.common.collect.Lists;
import com.google.common.io.Resources;

import io.undertow.Undertow;
import io.undertow.server.handlers.CanonicalPathHandler;
import io.undertow.server.handlers.PathHandler;
import io.undertow.util.Methods;

public class VocabularyPublisher {

    private static final class DefaultDocumentationProviderFactory implements DocumentationProviderFactory {

        private final DocumentationStrategy strategy;

        private DefaultDocumentationProviderFactory(final DocumentationStrategy strategy) {
            this.strategy = strategy;
        }

        @Override
        public DocumentationProvider create(final Module module) {
            return ImmutableDocumentationProvider.create(this.strategy, module);
        }

    }

    private static final class DefaultDocumentationDeploymentFactory implements DocumentationDeploymentFactory {

        private final DocumentationStrategy strategy;

        private DefaultDocumentationDeploymentFactory(final DocumentationStrategy strategy) {
            this.strategy = strategy;
        }

        @Override
        public DocumentationDeployment create(final Module module) {
            return ImmutableDocumentationDeployment.create(this.strategy, module);
        }

    }

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

    private static final MediaType HTML = MediaTypes.of("text", "html");

    public static void main(final String... args) throws FileNotFoundException, IOException {
        if (args.length != 1) {
            System.err.printf("Invalid argument number: 1 argument required (%d)%n", args.length);
            System.err.printf("USAGE: %s <path-to-config-file>%n",
                    AppAssembler.applicationName(VocabularyPublisher.class));
            System.err.printf(
                    "  <path-to-config-file> : Path Vocabulary Publisher configuration file is available.%n");
            Application.logContext(args);
            System.exit(-1);
        }
        System.out.printf("Vocabulary Publisher%s%n", serviceVersion());
        try {
            final Path configFile = Paths.get(args[0]);
            final PublisherConfig config = ConfigurationFactory.load(
                    Resources.toString(configFile.toUri().toURL(), StandardCharsets.UTF_8), PublisherConfig.class);
            System.out.printf("- Base URI: %s%n", config.getBase());
            System.out.printf("- Server..: %s:%s%n", config.getServer().getHost(), config.getServer().getPort());
            System.out.printf("- Source directory: %s%n", config.getRoot().toAbsolutePath());
            setup(config);
        } catch (final InvalidPathException e) {
            System.err.printf("%s is not a valid root path (%s)%n", args[0], e.getMessage());
            System.exit(-2);
        } catch (final IOException e) {
            System.err.printf("Could not explore modules (%s)%n", e.getMessage());
            System.exit(-3);
        } catch (final RuntimeException e) {
            System.err.println("Unexpected publisher failure\n. Full stacktrace follows");
            e.printStackTrace(System.err);
            System.exit(-4);
        }
    }

    private static void setup(final PublisherConfig config) throws IOException {
        final Result<Catalog> result = Catalogs.loadFrom(config.getRoot(), config.getBase());
        if (result.isAvailable()) {
            final Catalog catalog = result.get();
            showCatalog(catalog);
            try {
                publish(catalog, config.getBase().getPath(), ".cache", config.getServer().getPort(),
                        config.getServer().getHost(), getDocumentationStrategy(config));
            } finally {
                System.out.println("Publisher terminated.");
            }
        } else {
            System.err.println("Could not prepare catalog:\n" + result);
            System.exit(-5);
        }
    }

    private static String serviceVersion() {
        final String build = serviceBuild();
        final String version = System.getProperty("service.version", "");
        if (version.isEmpty()) {
            return version;
        }
        return " v" + version + build;
    }

    private static String serviceBuild() {
        String build = System.getProperty("service.build", "");
        if (!build.isEmpty()) {
            build = "-b" + build;
        }
        return build;
    }

    private static void showCatalog(final Catalog catalog) {
        LOGGER.debug("Found {} modules", catalog.size());
        final List<Module> externals = Lists.newArrayList();
        for (final String moduleId : catalog.modules()) {
            final Module module = catalog.get(moduleId);
            if (module.isExternal()) {
                externals.add(module);
                continue;
            }
            showModule(module);
        }
        for (final Module module : externals) {
            showDependency(module);
        }
    }

    private static void showDependency(final Module module) {
        final StringBuilder builder = new StringBuilder("- [EXTERNAL] Module '").append(module.location())
                .append("' [").append(module.relativePath()).append("] (").append(module.format().getName())
                .append(")");
        if (module.isOntology()) {
            builder.append(" refers to ");
            if (module.isVersion()) {
                builder.append("version '").append(module.versionIRI()).append("' of ");
            }
            builder.append("IRI '").append(module.ontology()).append("'");
            LOGGER.debug(builder.toString());
        }
    }

    private static void showModule(final Module module) {
        LOGGER.debug("- Module '{}':", module.location());
        LOGGER.debug("  + Relative path: {}", module.relativePath());
        LOGGER.debug("  + Format: {}", module.format().getName());
        if (module.isOntology()) {
            LOGGER.debug("  + Ontology:");
            LOGGER.debug("    * IRI: {}", module.ontology());
            if (module.isVersion()) {
                LOGGER.debug("    * VersionIRI: {}", module.versionIRI());
            }
            if (module.hasImports()) {
                LOGGER.debug("    * Imports:");
                for (final String importedModule : module.imports()) {
                    LOGGER.debug("      - {}", importedModule);
                }
            }
        }
    }

    private static void publish(final Catalog catalog, final String basePath, final String serializationCachePath,
            final int port, final String host, final DocumentationStrategy strategy) throws IOException {
        LOGGER.debug("* Publishing vocabularies under {}", basePath);
        final PathHandler pathHandler = path();
        // Module serializations
        final SerializationManager manager = publishSerializations(catalog, pathHandler, serializationCachePath);
        // Catalog documentation
        final DocumentationDeploymentFactory factory = publishDocumentation(catalog, pathHandler, strategy);
        // Canonical namespaces
        publishCanonicalNamespace(catalog, basePath, pathHandler, manager, factory);
        final Undertow server = Undertow.builder().addHttpListener(port, host)
                .setHandler(new CanonicalPathHandler(pathHandler)).build();
        server.start();
        awaitTerminationRequest();
        server.stop();
    }

    private static void publishCanonicalNamespace(final Catalog catalog, final String basePath,
            final PathHandler pathHandler, final SerializationManager manager,
            final DocumentationDeploymentFactory factory) {
        final ContentNegotiationHandler contentNegotiation = contentNegotiation()
                .negotiate(negotiableModuleContent(), new ModuleRepresentionGenerator(manager));
        if (factory != null) {
            contentNegotiation.negotiate(NegotiableContent.newInstance().support(HTML),
                    new ModuleDocumentationRedirector(factory));
        }
        pathHandler.addPrefixPath(basePath,
                moduleReverseProxy(catalog, methodController(contentNegotiation).allow(Methods.GET)));
    }

    private static DocumentationDeploymentFactory publishDocumentation(final Catalog catalog,
            final PathHandler pathHandler, final DocumentationStrategy strategy) {
        if (strategy == null) {
            return null;
        }
        final DocumentationDeploymentFactory deploymentFactory = new DefaultDocumentationDeploymentFactory(
                strategy);
        final DocumentationDeployer deployer = DocumentationDeployer.create(deploymentFactory,
                new DefaultDocumentationProviderFactory(strategy));
        deployer.deploy(catalog, pathHandler);
        return deploymentFactory;
    }

    private static SerializationManager publishSerializations(final Catalog catalog, final PathHandler pathHandler,
            final String cachePath) throws IOException {
        final SerializationManager manager = SerializationManager.create(catalog, Paths.get(cachePath));
        for (final String moduleId : catalog.modules()) {
            final Module module = catalog.get(moduleId);
            LOGGER.debug("- Module ({}):", module.implementationIRI(), module.location());
            for (final Format format : Format.values()) {
                final String resourceName = module.relativePath() + "." + format.fileExtension();
                final URI location = catalog.getBase().resolve(resourceName);
                LOGGER.debug("  + {} : {} --> {} ({})", format.getName(), location, location.getPath(),
                        manager.getSerialization(module, format).toAbsolutePath());
                pathHandler
                        .addExactPath(location.getPath(),
                                methodController(
                                        contentNegotiation()
                                                .negotiate(
                                                        NegotiableContent.newInstance()
                                                                .support(Formats.toMediaType(format))
                                                                .support(CharacterEncodings
                                                                        .of(StandardCharsets.UTF_8))
                                                                .support(CharacterEncodings
                                                                        .of(StandardCharsets.ISO_8859_1))
                                                                .support(CharacterEncodings
                                                                        .of(StandardCharsets.US_ASCII)),
                                                        new SerializationHandler(manager, module, format)))
                                                                .allow(Methods.GET));
            }
        }
        return manager;
    }

    private static DocumentationStrategy getDocumentationStrategy(final PublisherConfig config) {
        final DocumentationConfig docConfig = getDocumentationConfig(config);
        if (docConfig == null) {
            return null;
        }
        return new DocumentationStrategy(docConfig.getRoot(), docConfig.getRelativePath());
    }

    private static DocumentationConfig getDocumentationConfig(final PublisherConfig config) {
        final DocumentationConfig docConfig = config.extension(DocumentationConfig.class);
        if (docConfig == null) {
            return null;
        }
        if (docConfig.getRoot() == null) {
            docConfig.setRoot(config.getRoot().getParent().resolve("docs/"));
        }
        if (docConfig.getRelativePath() == null) {
            docConfig.setRelativePath("html");
        }
        System.out.printf("- Documentation directory....: %s%n", docConfig.getRoot().toAbsolutePath());
        System.out.printf("- Documentation relative path: %s%n", docConfig.getRelativePath());
        return docConfig;
    }

    private static NegotiableContent negotiableModuleContent() {
        return NegotiableContent.newInstance().support(Formats.toMediaType(Format.TURTLE))
                .support(Formats.toMediaType(Format.RDF_XML)).support(Formats.toMediaType(Format.JSON_LD))
                .support(CharacterEncodings.of(StandardCharsets.UTF_8))
                .support(CharacterEncodings.of(StandardCharsets.ISO_8859_1))
                .support(CharacterEncodings.of(StandardCharsets.US_ASCII));
    }

    private static void awaitTerminationRequest() {
        System.out.println("Hit <ENTER> to exit...");
        try (final Scanner scanner = new Scanner(System.in)) {
            String readString = scanner.nextLine();
            while (readString != null) {
                if (readString.isEmpty()) {
                    break;
                }
                if (scanner.hasNextLine()) {
                    readString = scanner.nextLine();
                } else {
                    readString = null;
                }
            }
        }
        System.out.println("<ENTER> detected.");
    }

}