org.terracotta.config.TCConfigurationParser.java Source code

Java tutorial

Introduction

Here is the source code for org.terracotta.config.TCConfigurationParser.java

Source

/*
 * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Covered Software is Terracotta Configuration.
 *
 * The Initial Developer of the Covered Software is
 * Terracotta, Inc., a Software AG company
 *
 */

package org.terracotta.config;

import org.terracotta.config.service.ServiceConfigParser;
import org.terracotta.config.util.DefaultSubstitutor;
import org.apache.commons.io.IOUtils;
import org.terracotta.config.util.ParameterSubstitutor;
import org.terracotta.entity.ServiceProviderConfiguration;
import org.w3c.dom.Element;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.List;

public class TCConfigurationParser {

    private static final SchemaFactory XSD_SCHEMA_FACTORY = SchemaFactory
            .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    private static final URL TERRACOTTA_XML_SCHEMA = TCConfigurationParser.class.getResource("/terracotta.xsd");
    private static final String WILDCARD_IP = "0.0.0.0";
    public static final short DEFAULT_GROUPPORT_OFFSET_FROM_TSAPORT = 20;
    public static final short DEFAULT_MANAGEMENTPORT_OFFSET_FROM_TSAPORT = 30;
    public static final int MIN_PORTNUMBER = 0x0FFF;
    public static final int MAX_PORTNUMBER = 0xFFFF;
    public static final String DEFAULT_LOGS = "logs";
    public static final String DEFAULT_DATA = "data";
    public static final String DEFAULT_DATA_BACKUP = "data-backup";

    private static final Map<URI, ServiceConfigParser> serviceParsers = new HashMap<>();

    @SuppressWarnings("unchecked")
    private static TcConfiguration parseStream(InputStream in, ErrorHandler eh, String source, ClassLoader loader)
            throws IOException, SAXException {
        Collection<Source> schemaSources = new ArrayList<>();

        for (ServiceConfigParser parser : loadConfigurationParserClasses(loader)) {
            schemaSources.add(parser.getXmlSchema());
            serviceParsers.put(parser.getNamespace(), parser);
        }
        schemaSources.add(new StreamSource(TERRACOTTA_XML_SCHEMA.openStream()));

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setIgnoringComments(true);
        factory.setIgnoringElementContentWhitespace(true);
        factory.setSchema(XSD_SCHEMA_FACTORY.newSchema(schemaSources.toArray(new Source[schemaSources.size()])));

        final DocumentBuilder domBuilder;
        try {
            domBuilder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new AssertionError(e);
        }
        domBuilder.setErrorHandler(eh);
        final Element config = domBuilder.parse(in).getDocumentElement();

        try {
            JAXBContext jc = JAXBContext.newInstance("org.terracotta.config",
                    TCConfigurationParser.class.getClassLoader());
            Unmarshaller u = jc.createUnmarshaller();

            TcConfig tcConfig = u.unmarshal(config, TcConfig.class).getValue();
            if (tcConfig.getServers() == null) {
                Servers servers = new Servers();
                tcConfig.setServers(servers);
            }

            if (tcConfig.getServers().getServer().isEmpty()) {
                tcConfig.getServers().getServer().add(new Server());
            }
            DefaultSubstitutor.applyDefaults(tcConfig);
            applyPlatformDefaults(tcConfig, source);

            Map<String, Map<String, ServiceOverride>> serviceOverrides = new HashMap<>();
            for (Server server : tcConfig.getServers().getServer()) {
                if (server.getServiceOverrides() != null
                        && server.getServiceOverrides().getServiceOverride() != null) {
                    for (ServiceOverride serviceOverride : server.getServiceOverrides().getServiceOverride()) {
                        String id = ((Service) serviceOverride.getOverrides()).getId();
                        if (serviceOverrides.get(id) == null) {
                            serviceOverrides.put(id, new HashMap<>());
                        }
                        serviceOverrides.get(id).put(server.getName(), serviceOverride);
                    }
                }
            }

            Map<String, List<ServiceProviderConfiguration>> serviceConfigurations = new HashMap<>();
            if (tcConfig.getServices() != null && tcConfig.getServices().getService() != null) {
                //now parse the service configuration.
                for (Service service : tcConfig.getServices().getService()) {
                    Element element = service.getAny();
                    if (element != null) {
                        URI namespace = URI.create(element.getNamespaceURI());
                        ServiceConfigParser parser = serviceParsers.get(namespace);
                        if (parser == null) {
                            throw new TCConfigurationSetupException("Can't find parser for service " + namespace);
                        }
                        ServiceProviderConfiguration serviceProviderConfiguration = parser.parse(element, source);
                        for (Server server : tcConfig.getServers().getServer()) {
                            if (serviceConfigurations.get(server.getName()) == null) {
                                serviceConfigurations.put(server.getName(), new ArrayList<>());
                            }
                            if (serviceOverrides.get(service.getId()) != null
                                    && serviceOverrides.get(service.getId()).containsKey(server.getName())) {
                                Element overrideElement = serviceOverrides.get(service.getId())
                                        .get(server.getName()).getAny();
                                if (overrideElement != null) {
                                    serviceConfigurations.get(server.getName())
                                            .add(parser.parse(overrideElement, source));
                                }
                            } else {
                                serviceConfigurations.get(server.getName()).add(serviceProviderConfiguration);
                            }
                        }
                    }
                }
            }

            return new TcConfiguration(tcConfig, source, serviceConfigurations);
        } catch (JAXBException e) {
            throw new TCConfigurationSetupException(e);
        }
    }

    private static void applyPlatformDefaults(TcConfig tcConfig, String source) {
        for (Server server : tcConfig.getServers().getServer()) {
            TCConfigurationParser.setDefaultBind(server);
            TCConfigurationParser.initializeTsaPort(server);
            TCConfigurationParser.initializeManagementPort(server);
            TCConfigurationParser.initializeTsaGroupPort(server);
            TCConfigurationParser.initializeNameAndHost(server);
            TCConfigurationParser.initializeDataDirectory(server, source);
            TCConfigurationParser.initializeDataBackupDirectory(server, source);
            TCConfigurationParser.initializeLogsDirectory(server, source);
        }
    }

    private static void initializeTsaPort(Server server) {
        if (server.getTsaPort() == null) {
            server.setTsaPort(new BindPort());
        }
        if (server.getTsaPort().getBind() == null) {
            server.getTsaPort().setBind(server.getBind());
        }
    }

    private static void initializeDataBackupDirectory(Server server, String source) {
        if (server.getDataBackup() == null) {
            return;
        }
        server.setDataBackup(getAbsolutePath(ParameterSubstitutor.substitute(server.getDataBackup()),
                new File(source != null ? source : ".")));
    }

    private static void initializeDataDirectory(Server server, String source) {
        if (server.getData() == null) {
            server.setData(DEFAULT_DATA);
        }
        server.setData(getAbsolutePath(ParameterSubstitutor.substitute(server.getData()),
                new File(source != null ? source : ".")));
    }

    private static void initializeLogsDirectory(Server server, String source) {
        if (server.getLogs() == null) {
            server.setLogs(DEFAULT_LOGS);
        }
        server.setLogs(getAbsolutePath(ParameterSubstitutor.substitute(server.getLogs()),
                new File(source != null ? source : ".")));
    }

    private static String getAbsolutePath(String substituted, File directoryLoadedFrom) {
        File out = new File(substituted);
        if (!out.isAbsolute()) {
            out = new File(directoryLoadedFrom, substituted);
        }

        return out.getAbsolutePath();
    }

    private static void initializeManagementPort(Server server) {
        if (server.getManagementPort() == null) {
            BindPort managementPort = new BindPort();
            server.setManagementPort(managementPort);
            int defaultManagementPort = computeManagementPortFromTSAPort(server.getTsaPort().getValue());

            managementPort.setValue(defaultManagementPort);
            managementPort.setBind(server.getBind());
        } else if (server.getManagementPort().getBind() == null) {
            server.getManagementPort().setBind(server.getBind());
        }
    }

    public static int computeManagementPortFromTSAPort(int tsaPort) {
        int tempPort = tsaPort + DEFAULT_MANAGEMENTPORT_OFFSET_FROM_TSAPORT;
        return ((tempPort <= MAX_PORTNUMBER) ? tempPort : (tempPort % MAX_PORTNUMBER) + MIN_PORTNUMBER);
    }

    private static void initializeTsaGroupPort(Server server) {
        if (server.getTsaGroupPort() == null) {
            BindPort l2GrpPort = new BindPort();
            server.setTsaGroupPort(l2GrpPort);
            int tempGroupPort = server.getTsaPort().getValue() + DEFAULT_GROUPPORT_OFFSET_FROM_TSAPORT;
            int defaultGroupPort = ((tempGroupPort <= MAX_PORTNUMBER) ? (tempGroupPort)
                    : (tempGroupPort % MAX_PORTNUMBER) + MIN_PORTNUMBER);
            l2GrpPort.setValue(defaultGroupPort);
            l2GrpPort.setBind(server.getBind());
        } else if (server.getTsaGroupPort().getBind() == null) {
            server.getTsaGroupPort().setBind(server.getBind());
        }
    }

    private static void initializeNameAndHost(Server server) {
        if (server.getHost() == null || server.getHost().trim().length() == 0) {
            if (server.getName() == null) {
                server.setHost("%i");
            } else {
                server.setHost(server.getName());
            }
        }

        if (server.getName() == null || server.getName().trim().length() == 0) {
            int tsaPort = server.getTsaPort().getValue();
            server.setName(server.getHost() + (tsaPort > 0 ? ":" + tsaPort : ""));
        }

        // CDV-77: add parameter expansion to the <server> attributes ('host' and 'name')
        server.setHost(ParameterSubstitutor.substitute(server.getHost()));
        server.setName(ParameterSubstitutor.substitute(server.getName()));
    }

    private static void setDefaultBind(Server s) {
        if (s.getBind() == null || s.getBind().trim().length() == 0) {
            s.setBind(WILDCARD_IP);
        }
        s.setBind(ParameterSubstitutor.substitute(s.getBind()));
    }

    private static TcConfiguration convert(InputStream in, String path, ClassLoader loader)
            throws IOException, SAXException {
        byte[] data = new byte[in.available()];
        in.read(data);
        in.close();
        ByteArrayInputStream bais = new ByteArrayInputStream(data);

        return parseStream(bais, RethrowErrorHandler.INSTANCE, path, loader);
    }

    public static TcConfiguration parse(File f) throws IOException, SAXException {
        return parse(f, Thread.currentThread().getContextClassLoader());
    }

    public static TcConfiguration parse(File file, ClassLoader loader) throws IOException, SAXException {
        FileInputStream in = null;

        try {
            in = new FileInputStream(file);
            return convert(in, file.getParent(), loader);
        } finally {

            IOUtils.closeQuietly(in);
        }
    }

    public static TcConfiguration parse(String xmlText) throws IOException, SAXException {
        return parse(xmlText, Thread.currentThread().getContextClassLoader());
    }

    public static TcConfiguration parse(String xmlText, ClassLoader loader) throws IOException, SAXException {
        return convert(new ByteArrayInputStream(xmlText.getBytes()), null, loader);
    }

    public static TcConfiguration parse(InputStream stream) throws IOException, SAXException {
        return parse(stream, Thread.currentThread().getContextClassLoader());
    }

    public static TcConfiguration parse(InputStream stream, ClassLoader loader) throws IOException, SAXException {
        return convert(stream, null, loader);
    }

    public static TcConfiguration parse(URL stream) throws IOException, SAXException {
        return parse(stream, Thread.currentThread().getContextClassLoader());
    }

    public static TcConfiguration parse(URL url, ClassLoader loader) throws IOException, SAXException {
        return convert(url.openStream(), url.getPath(), loader);
    }

    public static TcConfiguration parse(InputStream in, Collection<SAXParseException> errors, String source)
            throws IOException, SAXException {
        return parse(in, errors, source, Thread.currentThread().getContextClassLoader());
    }

    public static TcConfiguration parse(InputStream in, Collection<SAXParseException> errors, String source,
            ClassLoader loader) throws IOException, SAXException {
        return parseStream(in, new CollectingErrorHandler(errors), source, loader);
    }

    private static class CollectingErrorHandler implements ErrorHandler {

        private final Collection<SAXParseException> errors;

        CollectingErrorHandler(Collection<SAXParseException> errors) {
            this.errors = errors;
        }

        @Override
        public void error(SAXParseException exception) throws SAXException {
            errors.add(exception);
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
            errors.add(exception);
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
            errors.add(exception);
        }

    }

    private static class RethrowErrorHandler implements ErrorHandler {

        public static final ErrorHandler INSTANCE = new RethrowErrorHandler();

        @Override
        public void warning(SAXParseException exception) throws SAXException {
            throw exception;
        }

        @Override
        public void error(SAXParseException exception) throws SAXException {
            throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
            throw exception;
        }
    }

    private static ServiceLoader<ServiceConfigParser> loadConfigurationParserClasses(ClassLoader loader) {
        return ServiceLoader.load(ServiceConfigParser.class, loader);
    }

}