org.kaaproject.kaa.server.control.service.sdk.CppSdkGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.kaaproject.kaa.server.control.service.sdk.CppSdkGenerator.java

Source

/*
 * Copyright 2014-2016 CyberVision, Inc.
 *
 * 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.
 */

package org.kaaproject.kaa.server.control.service.sdk;

import org.apache.avro.Schema;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.compressors.CompressorInputStream;
import org.apache.commons.compress.compressors.CompressorOutputStream;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.kaaproject.kaa.common.dto.admin.SdkProfileDto;
import org.kaaproject.kaa.common.dto.file.FileData;
import org.kaaproject.kaa.server.common.Environment;
import org.kaaproject.kaa.server.common.Version;
import org.kaaproject.kaa.server.common.zk.ServerNameUtil;
import org.kaaproject.kaa.server.common.zk.gen.BootstrapNodeInfo;
import org.kaaproject.kaa.server.common.zk.gen.TransportMetaData;
import org.kaaproject.kaa.server.common.zk.gen.VersionConnectionInfoPair;
import org.kaaproject.kaa.server.control.service.sdk.compress.TarEntryData;
import org.kaaproject.kaa.server.control.service.sdk.event.CppEventSourcesGenerator;
import org.kaaproject.kaa.server.control.service.sdk.event.EventFamilyMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * The Class CppSdkGenerator.
 */
public class CppSdkGenerator extends SdkGenerator {

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

    private static final String CPP_SDK_DIR = "sdk/cpp";

    private static final String CPP_SDK_PREFIX = "kaa-cpp-ep-sdk-";

    private static final String SDK_VAR = "%{application.sdk_token}";

    private static final String SDK_PROFILE_VERSION_VAR = "%{application.profile_version}";

    private static final String CLIENT_PUB_KEY_LOCATION_VAR = "%{application.public_key_location}";

    private static final String CLIENT_PRIV_KEY_LOCATION_VAR = "%{application.private_key_location}";

    private static final String CLIENT_STATUS_FILE_LOCATION_VAR = "%{application.status_file_location}";

    private static final String POLLING_PERIOD_SECONDS_VAR = "%{application.polling_period_seconds}";

    private static final String BOOTSTRAP_SERVERS_INFO_VAR = "%{bootstrap_servers_info}";

    private static final String CONFIG_DATA_DEFAULT_VAR = "%{config.data.default}";

    private static final String BUILD_VERSION = "%{build.version}";

    private static final String BUILD_COMMIT_HASH = "%{build.commit_hash}";

    private static final String DEFAULT_USER_VERIFIER_TOKEN = "%{user_verifier_token}";

    private static final String SDK_DEFAULTS_TEMPLATE = "sdk/cpp/KaaDefaults.cpp.template";
    private static final String SDK_DEFAULTS_PATH = "impl/KaaDefaults.cpp";

    private static final String RECORD_CLASS_NAME_VAR = "%{record_class_name}";

    private static final String PROFILE_SCHEMA_AVRO_SRC = "avro/profile.avsc";

    private static final String PROFILE_DEFINITIONS_TEMPLATE = "sdk/cpp/profile/ProfileDefinitions.hpp.template";

    private static final String PROFILE_DEFINITIONS_PATH = "kaa/profile/gen/ProfileDefinitions.hpp";

    private static final String LOG_SCHEMA_AVRO_SRC = "avro/log.avsc";
    private static final String LOG_DEFINITIONS_TEMPLATE = "sdk/cpp/log/LogDefinitions.hpp.template";
    private static final String LOG_DEFINITIONS_PATH = "kaa/log/gen/LogDefinitions.hpp";

    private static final String NOTIFICATION_SCHEMA_AVRO_SRC = "avro/notification.avsc";

    private static final String NOTIFICATION_DEFINITIONS_TEMPLATE = "sdk/cpp/notification/NotificationDefinitions.hpp.template";

    private static final String NOTIFICATION_DEFINITIONS_PATH = "kaa/notification/gen/NotificationDefinitions.hpp";

    private static final String CONFIGURATION_SCHEMA_AVRO_SRC = "avro/configuration.avsc";

    private static final String CONFIGURATION_DEFINITIONS_TEMPLATE = "sdk/cpp/configuration/ConfigurationDefinitions.hpp.template";

    private static final String CONFIGURATION_DEFINITIONS_PATH = "kaa/configuration/gen/ConfigurationDefinitions.hpp";

    /**
     * Replace string.
     *
     * @param body     the body
     * @param variable the variable
     * @param value    the value
     * @return the string
     */
    private static String replaceVar(String body, String variable, String value) {
        return body.replace(variable, value);
    }

    /* (non-Javadoc)
     * @see org.kaaproject.kaa.server.control.service.sdk.SdkGenerator#generateSdk(
      * java.lang.String, java.util.List,
      * java.lang.String, int,
      * int, int,
      * java.lang.String, java.lang.String,
      * java.lang.String, byte[], java.util.List)
     */
    @Override
    public FileData generateSdk(String buildVersion, List<BootstrapNodeInfo> bootstrapNodes,
            SdkProfileDto sdkProfile, String profileSchemaBody, String notificationSchemaBody,
            String configurationProtocolSchemaBody, String configurationBaseSchema, byte[] defaultConfigurationData,
            List<EventFamilyMetadata> eventFamilies, String logSchemaBody) throws Exception {

        String sdkToken = sdkProfile.getToken();
        Integer configurationSchemaVersion = sdkProfile.getConfigurationSchemaVersion();
        final Integer profileSchemaVersion = sdkProfile.getProfileSchemaVersion();
        Integer notificationSchemaVersion = sdkProfile.getNotificationSchemaVersion();
        Integer logSchemaVersion = sdkProfile.getLogSchemaVersion();
        String defaultVerifierToken = sdkProfile.getDefaultVerifierToken();

        String sdkTemplateLocation = Environment.getServerHomeDir() + "/" + CPP_SDK_DIR + "/" + CPP_SDK_PREFIX
                + buildVersion + ".tar.gz";

        LOG.debug("Lookup Java SDK template: {}", sdkTemplateLocation);

        CompressorStreamFactory csf = new CompressorStreamFactory();
        ArchiveStreamFactory asf = new ArchiveStreamFactory();

        CompressorInputStream cis = csf.createCompressorInputStream(CompressorStreamFactory.GZIP,
                new FileInputStream(sdkTemplateLocation));

        final ArchiveInputStream templateArchive = asf.createArchiveInputStream(ArchiveStreamFactory.TAR, cis);

        ByteArrayOutputStream sdkOutput = new ByteArrayOutputStream();
        CompressorOutputStream cos = csf.createCompressorOutputStream(CompressorStreamFactory.GZIP, sdkOutput);

        ArchiveOutputStream sdkFile = asf.createArchiveOutputStream(ArchiveStreamFactory.TAR, cos);

        Map<String, TarEntryData> replacementData = new HashMap<>();

        List<TarEntryData> cppSources = new ArrayList<>();

        // TODO: remove all version fields and add single sdkToken field
        // create entry for default properties
        TarArchiveEntry entry = new TarArchiveEntry(SDK_DEFAULTS_PATH);
        byte[] data = generateKaaDefaults(bootstrapNodes, sdkToken, defaultConfigurationData, defaultVerifierToken);

        entry.setSize(data.length);
        TarEntryData tarEntry = new TarEntryData(entry, data);
        cppSources.add(tarEntry);

        Map<String, String> profileVars = new HashMap<>();
        profileVars.put(SDK_PROFILE_VERSION_VAR, profileSchemaVersion.toString());
        cppSources.addAll(processFeatureSchema(profileSchemaBody, PROFILE_SCHEMA_AVRO_SRC,
                PROFILE_DEFINITIONS_TEMPLATE, PROFILE_DEFINITIONS_PATH, profileVars));

        cppSources.addAll(processFeatureSchema(notificationSchemaBody, NOTIFICATION_SCHEMA_AVRO_SRC,
                NOTIFICATION_DEFINITIONS_TEMPLATE, NOTIFICATION_DEFINITIONS_PATH, null));
        cppSources.addAll(processFeatureSchema(logSchemaBody, LOG_SCHEMA_AVRO_SRC, LOG_DEFINITIONS_TEMPLATE,
                LOG_DEFINITIONS_PATH, null));
        cppSources.addAll(processFeatureSchema(configurationBaseSchema, CONFIGURATION_SCHEMA_AVRO_SRC,
                CONFIGURATION_DEFINITIONS_TEMPLATE, CONFIGURATION_DEFINITIONS_PATH, null));

        if (eventFamilies != null && !eventFamilies.isEmpty()) {
            cppSources.addAll(CppEventSourcesGenerator.generateEventSources(eventFamilies));
        }

        for (TarEntryData entryData : cppSources) {
            replacementData.put(entryData.getEntry().getName(), entryData);
        }

        ArchiveEntry archiveEntry;
        while ((archiveEntry = templateArchive.getNextEntry()) != null) {
            if (!archiveEntry.isDirectory()) {
                if (replacementData.containsKey(archiveEntry.getName())) {
                    TarEntryData entryData = replacementData.remove(archiveEntry.getName());
                    sdkFile.putArchiveEntry(entryData.getEntry());
                    sdkFile.write(entryData.getData());
                } else {
                    sdkFile.putArchiveEntry(archiveEntry);
                    IOUtils.copy(templateArchive, sdkFile);
                }
            } else {
                sdkFile.putArchiveEntry(archiveEntry);
            }
            sdkFile.closeArchiveEntry();
        }

        templateArchive.close();

        for (String entryName : replacementData.keySet()) {
            TarEntryData entryData = replacementData.get(entryName);
            sdkFile.putArchiveEntry(entryData.getEntry());
            sdkFile.write(entryData.getData());
            sdkFile.closeArchiveEntry();
        }

        sdkFile.finish();
        sdkFile.close();

        String sdkFileName = CPP_SDK_PREFIX + sdkProfile.getToken() + ".tar.gz";

        byte[] sdkData = sdkOutput.toByteArray();

        FileData sdk = new FileData();
        sdk.setFileName(sdkFileName);
        sdk.setFileData(sdkData);
        return sdk;
    }

    private List<TarEntryData> processFeatureSchema(String schemaBody, String schemaPath, String templatePath,
            String outputPath, Map<String, String> vars) throws IOException {
        List<TarEntryData> cppSources = new LinkedList<>();

        if (!StringUtils.isBlank(schemaBody)) {
            TarArchiveEntry entry = new TarArchiveEntry(schemaPath);
            byte[] data = schemaBody.getBytes();
            entry.setSize(data.length);
            TarEntryData tarEntry = new TarEntryData(entry, data);
            cppSources.add(tarEntry);

            Schema schema = new Schema.Parser().parse(schemaBody);
            String definitionsHpp = SdkGenerator.readResource(templatePath);
            entry = new TarArchiveEntry(outputPath);

            String templateStr = replaceVar(definitionsHpp, RECORD_CLASS_NAME_VAR, schema.getName());
            if (vars != null && !vars.isEmpty()) {
                for (Entry<String, String> var : vars.entrySet()) {
                    templateStr = replaceVar(templateStr, var.getKey(), var.getValue());
                }
            }

            byte[] definitionsData = templateStr.getBytes();
            entry.setSize(definitionsData.length);
            tarEntry = new TarEntryData(entry, definitionsData);
            cppSources.add(tarEntry);
        }

        return cppSources;
    }

    /**
     * Generate client properties.
     *
     * @param bootstrapNodes           the bootstrap nodes
     * @param sdkToken                 the app token
     * @param defaultConfigurationData the default configuration data
     * @return the byte[]
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private byte[] generateKaaDefaults(List<BootstrapNodeInfo> bootstrapNodes, String sdkToken,
            byte[] defaultConfigurationData, String defaultVerifierToken) throws IOException {
        String kaaDefaultsString = SdkGenerator.readResource(SDK_DEFAULTS_TEMPLATE);

        LOG.debug("[sdk generateClientProperties] bootstrapNodes.size(): {}", bootstrapNodes.size());

        kaaDefaultsString = replaceVar(kaaDefaultsString, BUILD_VERSION, Version.PROJECT_VERSION);
        kaaDefaultsString = replaceVar(kaaDefaultsString, BUILD_COMMIT_HASH, Version.COMMIT_HASH);

        kaaDefaultsString = replaceVar(kaaDefaultsString, SDK_VAR, sdkToken);

        kaaDefaultsString = replaceVar(kaaDefaultsString, POLLING_PERIOD_SECONDS_VAR, "5");

        kaaDefaultsString = replaceVar(kaaDefaultsString, CLIENT_PUB_KEY_LOCATION_VAR, "key.public");
        kaaDefaultsString = replaceVar(kaaDefaultsString, CLIENT_PRIV_KEY_LOCATION_VAR, "key.private");
        kaaDefaultsString = replaceVar(kaaDefaultsString, CLIENT_STATUS_FILE_LOCATION_VAR, "kaa.status");

        kaaDefaultsString = replaceVar(kaaDefaultsString, DEFAULT_USER_VERIFIER_TOKEN,
                defaultVerifierToken != null ? defaultVerifierToken : "");

        String bootstrapServers = "";

        LOG.debug("[sdk generateClientProperties] bootstrapNodes.size(): {}", bootstrapNodes.size());

        for (BootstrapNodeInfo node : bootstrapNodes) {
            List<TransportMetaData> supportedChannels = node.getTransports();

            int accessPointId = ServerNameUtil.crc32(node.getConnectionInfo());
            for (TransportMetaData transport : supportedChannels) {
                for (VersionConnectionInfoPair pair : transport.getConnectionInfo()) {
                    String serverPattern = "listOfServers.push_back(createTransportInfo(";
                    serverPattern += "0x" + Integer.toHexString(accessPointId);
                    serverPattern += ", ";
                    serverPattern += "0x" + Integer.toHexString(transport.getId());
                    serverPattern += ", ";
                    serverPattern += pair.getVersion();
                    serverPattern += ", \"";
                    serverPattern += Base64.encodeBase64String(pair.getConenctionInfo().array());
                    serverPattern += "\"";
                    serverPattern += "));\n";
                    bootstrapServers += serverPattern;
                }
            }
        }

        kaaDefaultsString = replaceVar(kaaDefaultsString, BOOTSTRAP_SERVERS_INFO_VAR, bootstrapServers);
        kaaDefaultsString = replaceVar(kaaDefaultsString, CONFIG_DATA_DEFAULT_VAR,
                Base64.encodeBase64String(defaultConfigurationData));

        return kaaDefaultsString.getBytes();
    }

}