com.sitewhere.configuration.ConfigurationMigrationSupport.java Source code

Java tutorial

Introduction

Here is the source code for com.sitewhere.configuration.ConfigurationMigrationSupport.java

Source

/*
 * Copyright (c) SiteWhere, LLC. All rights reserved. http://www.sitewhere.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package com.sitewhere.configuration;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.log4j.Logger;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import com.sitewhere.spi.SiteWhereException;
import com.sitewhere.spi.configuration.IGlobalConfigurationResolver;

/**
 * Helps with migrating from previous configuration layout.
 * 
 * @author Derek
 */
public class ConfigurationMigrationSupport {

    /** Static logger instance */
    private static Logger LOGGER = Logger.getLogger(ConfigurationMigrationSupport.class);

    /** Filenames for previous default tenant configurations */
    public static final String[] OLD_TEMPLATE_FILENAMES = { "sitewhere-tenant.xml", "tenant-template.xml" };

    /**
     * Detects whether using old configuration model and upgrades if necessary.
     * 
     * @param global
     * @throws SiteWhereException
     */
    public static void migrateProjectStructureIfNecessary(IGlobalConfigurationResolver global)
            throws SiteWhereException {
        File root = new File(global.getConfigurationRoot());
        if (!root.exists()) {
            throw new SiteWhereException("Configuration root does not exist.");
        }
        File templateFolder = new File(root, FileSystemTenantConfigurationResolver.DEFAULT_TENANT_TEMPLATE_FOLDER);
        if (!templateFolder.exists()) {
            if (!templateFolder.mkdir()) {
                throw new SiteWhereException("Unable to create template folder.");
            }
            for (String filename : OLD_TEMPLATE_FILENAMES) {
                File templateFile = new File(root, filename);
                if (templateFile.exists()) {
                    migrateTemplateFile(root, templateFolder, filename);
                    migrateResources(root, templateFolder);
                    break;
                }
            }
        }

        // Migrate tenant configuration files to separate directories.
        File tenants = new File(root, FileSystemTenantConfigurationResolver.TENANTS_FOLDER);
        if (!tenants.exists()) {
            if (!tenants.mkdir()) {
                throw new SiteWhereException("Unable to create tenant resources folder.");
            }
        }
        Collection<File> oldConfigs = FileUtils.listFiles(root, FileFilterUtils.suffixFileFilter("-tenant.xml"),
                null);
        for (File oldConfig : oldConfigs) {
            int dash = oldConfig.getName().lastIndexOf('-');
            String tenantName = oldConfig.getName().substring(0, dash);
            File tenantFolder = new File(tenants, tenantName);
            try {
                FileUtils.copyDirectory(templateFolder, tenantFolder);
                File tenantConfig = new File(tenantFolder,
                        FileSystemTenantConfigurationResolver.DEFAULT_TENANT_CONFIGURATION_FILE + "."
                                + FileSystemTenantConfigurationResolver.TENANT_SUFFIX_ACTIVE);
                if (tenantConfig.exists()) {
                    tenantConfig.delete();
                }
                FileUtils.moveFile(oldConfig, tenantConfig);
                oldConfig.delete();
            } catch (IOException e) {
                throw new SiteWhereException("Unable to copy template folder for tenant.");
            }
        }
    }

    /**
     * Move template file and rename it.
     * 
     * @param root
     * @param templateFolder
     * @param templateFilename
     * @throws SiteWhereException
     */
    protected static void migrateTemplateFile(File root, File templateFolder, String templateFilename)
            throws SiteWhereException {
        try {
            File templateFile = new File(root, templateFilename);
            FileUtils.moveFileToDirectory(templateFile, templateFolder, false);
            File moved = new File(templateFolder, templateFilename);
            File updated = new File(templateFolder,
                    FileSystemTenantConfigurationResolver.DEFAULT_TENANT_CONFIGURATION_FILE + "."
                            + FileSystemTenantConfigurationResolver.TENANT_SUFFIX_ACTIVE);
            if (!moved.getAbsolutePath().equals(updated.getAbsolutePath())) {
                FileUtils.moveFile(moved, updated);
            }
        } catch (IOException e) {
            throw new SiteWhereException("Unable to migrate template file.", e);
        }
    }

    /**
     * Migrate assets and scripts into template directory.
     * 
     * @param root
     * @param templateFolder
     * @throws SiteWhereException
     */
    protected static void migrateResources(File root, File templateFolder) throws SiteWhereException {
        // Move assets.
        File assets = new File(root, "assets");
        if (assets.exists()) {
            try {
                File newAssets = new File(templateFolder, FileSystemTenantConfigurationResolver.ASSETS_FOLDER);
                FileUtils.moveDirectory(assets, newAssets);
            } catch (IOException e) {
                throw new SiteWhereException("Unable to move assets folder to template.");
            }
        }

        // Move Groovy scripts.
        File scripts = new File(templateFolder, FileSystemTenantConfigurationResolver.SCRIPTS_FOLDER);
        if (!scripts.exists()) {
            if (!scripts.mkdir()) {
                throw new SiteWhereException("Unable to create scripts folder in template folder.");
            }
            try {
                File groovy = new File(root, "groovy");
                if (groovy.exists()) {
                    File newGroovy = new File(scripts, "groovy");
                    FileUtils.moveDirectory(groovy, newGroovy);
                }
            } catch (IOException e) {
                throw new SiteWhereException("Unable to move assets folder to template.");
            }
        }
    }

    /**
     * Migrates a tenant configuration file to latest schema.
     * 
     * @param original
     * @return
     * @throws SiteWhereException
     */
    public static byte[] migrateTenantConfigurationIfNecessary(byte[] original) throws SiteWhereException {
        Document document = getConfigurationDocument(original);

        migrateTenantConfiguration(document);

        String updated = format(document);
        return updated.getBytes();
    }

    /**
     * Perform the migration work.
     * 
     * @param document
     * @throws SiteWhereException
     */
    protected static void migrateTenantConfiguration(Document document) throws SiteWhereException {
        Element beans = document.getDocumentElement();
        Element config = DomUtils.getChildElementByTagName(beans, "tenant-configuration");
        if (config == null) {
            throw new SiteWhereException("Tenant configuration element not found.");
        }
        Element dcomm = DomUtils.getChildElementByTagName(config, "device-communication");
        if (dcomm == null) {
            throw new SiteWhereException("Device communication element missing.");
        }
        Element asset = DomUtils.getChildElementByTagName(config, "asset-management");
        if (asset == null) {
            throw new SiteWhereException("Asset management element missing.");
        }
        Element eproc = DomUtils.getChildElementByTagName(config, "event-processing");
        if (eproc == null) {
            LOGGER.info("Event processing element missing. Migrating to new configuration format.");
            eproc = document.createElementNS(config.getNamespaceURI(), "event-processing");
            eproc.setPrefix(config.getPrefix());
            config.insertBefore(eproc, asset);

            moveEventProcessingElements(config, dcomm, eproc, document);
        }
        document.normalizeDocument();
    }

    /**
     * Moves event processing elements from previous locations into new element.
     * 
     * @param config
     * @param dcomm
     * @param eproc
     * @param document
     * @throws SiteWhereException
     */
    protected static void moveEventProcessingElements(Element config, Element dcomm, Element eproc,
            Document document) throws SiteWhereException {
        Element iproc = DomUtils.getChildElementByTagName(dcomm, "inbound-processing-strategy");
        if (iproc != null) {
            dcomm.removeChild(iproc);
            eproc.appendChild(iproc);
        }
        Element ichain = DomUtils.getChildElementByTagName(config, "inbound-processing-chain");
        if (ichain != null) {
            config.removeChild(ichain);
            eproc.appendChild(ichain);
        }
        Element oproc = DomUtils.getChildElementByTagName(dcomm, "outbound-processing-strategy");
        if (oproc != null) {
            dcomm.removeChild(oproc);
            eproc.appendChild(oproc);
        }
        Element ochain = DomUtils.getChildElementByTagName(config, "outbound-processing-chain");
        if (ochain != null) {
            config.removeChild(ochain);
            eproc.appendChild(ochain);
        }
        Element reg = DomUtils.getChildElementByTagName(dcomm, "registration");
        if (reg != null) {
            String qname = (reg.getPrefix() != null) ? (reg.getPrefix() + ":" + "device-services")
                    : "device-services";
            document.renameNode(reg, reg.getNamespaceURI(), qname);
        }
    }

    /**
     * Get configuration bytes as DOM document.
     * 
     * @param original
     * @return
     * @throws SiteWhereException
     */
    protected static Document getConfigurationDocument(byte[] original) throws SiteWhereException {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            return builder.parse(new InputSource(new ByteArrayInputStream(original)));
        } catch (Exception e) {
            throw new SiteWhereException("Unable to parse tenant configuration.", e);
        }
    }

    /**
     * Format the given XML document.
     * 
     * @param xml
     * @return
     * @throws SiteWhereException
     */
    public static String format(Document xml) throws SiteWhereException {
        try {
            Transformer tf = TransformerFactory.newInstance().newTransformer();
            tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            tf.setOutputProperty(OutputKeys.INDENT, "yes");
            tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            Writer out = new StringWriter();
            tf.transform(new DOMSource(xml), new StreamResult(out));
            return out.toString();
        } catch (Exception e) {
            throw new SiteWhereException("Unable to format XML document.", e);
        }
    }
}