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

Java tutorial

Introduction

Here is the source code for org.kaaproject.kaa.server.control.service.sdk.ObjCSdkGenerator.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.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.avro.avrogen.compiler.Compiler;
import org.kaaproject.kaa.avro.avrogen.compiler.ObjectiveCCompiler;
import org.kaaproject.kaa.common.dto.admin.SdkProfileDto;
import org.kaaproject.kaa.common.dto.file.FileData;
import org.kaaproject.kaa.server.common.Version;
import org.kaaproject.kaa.server.common.zk.gen.BootstrapNodeInfo;
import org.kaaproject.kaa.server.control.service.sdk.compress.TarEntryData;
import org.kaaproject.kaa.server.control.service.sdk.event.EventFamilyMetadata;
import org.kaaproject.kaa.server.control.service.sdk.event.ObjCEventClassesGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

public class ObjCSdkGenerator extends SdkGenerator {

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

    private static final String CONFIGURATION_DIR = "configuration/";
    private static final String NOTIFICATION_DIR = "notification/";
    private static final String PROFILE_DIR = "profile/";
    private static final String LOG_DIR = "log/";
    private static final String EVENT_DIR = "event/";

    private static final String SDK_TEMPLATE_DIR = "sdk/objc/";

    private static final String CONFIGURATION_TEMPLATE_DIR = SDK_TEMPLATE_DIR + CONFIGURATION_DIR;
    private static final String NOTIFICATION_TEMPLATE_DIR = SDK_TEMPLATE_DIR + NOTIFICATION_DIR;
    private static final String PROFILE_TEMPLATE_DIR = SDK_TEMPLATE_DIR + PROFILE_DIR;
    private static final String LOG_TEMPLATE_DIR = SDK_TEMPLATE_DIR + LOG_DIR;
    private static final String EVENT_TEMPLATE_DIR = SDK_TEMPLATE_DIR + EVENT_DIR;

    private static final String SDK_PREFIX = "kaa-objc-ep-sdk-";

    private static final String KAA_ROOT = "Kaa/";

    private static final String KAA_GEN_FOLDER = KAA_ROOT + "avro/gen/";

    private static final String KAA_SOURCE_PREFIX = "KAA";

    private static final String TEMPLATE_SUFFIX = ".template";
    private static final String _H = ".h";
    private static final String _M = ".m";

    private static final String NOTIFICATION_GEN = "NotificationGen";
    private static final String PROFILE_GEN = "ProfileGen";
    private static final String LOG_GEN = "LogGen";
    private static final String CONFIGURATION_GEN = "ConfigurationGen";

    private static final String KAA_DEFAULTS = "KaaDefaults.h";
    private static final String KAA_CLIENT = "KaaClient.h";
    private static final String BASE_KAA_CLIENT = "BaseKaaClient";
    private static final String CONFIGURATION_COMMON = "ConfigurationCommon.h";
    private static final String CONFIGURATION_MANAGER_IMPL = "ResyncConfigurationManager";
    private static final String CONFIGURATION_DESERIALIZER = "ConfigurationDeserializer";
    private static final String NOTIFICATION_COMMON = "NotificationCommon";
    private static final String PROFILE_COMMON = "ProfileCommon";
    private static final String LOG_RECORD = "LogRecord";
    private static final String LOG_COLLECTOR_INTERFACE = "LogCollector.h";
    private static final String LOG_COLLECTOR_SOURCE = "DefaultLogCollector";
    private static final String USER_VERIFIER_CONSTANTS = "UserVerifierConstants.h";

    private static final String PROFILE_CLASS_VAR = "\\$\\{profile_class\\}";
    private static final String CONFIGURATION_CLASS_VAR = "\\$\\{configuration_class\\}";
    private static final String NOTIFICATION_CLASS_VAR = "\\$\\{notification_class\\}";
    private static final String LOG_RECORD_CLASS_VAR = "\\$\\{log_record_class\\}";

    private static final String DEFAULT_USER_VERIFIER_TOKEN_VAR = "\\$\\{default_user_verifier_token\\}";

    private static final String PROFILE_NAMESPACE = KAA_SOURCE_PREFIX + "Profile";
    private static final String LOGGING_NAMESPACE = KAA_SOURCE_PREFIX + "Logging";
    private static final String CONFIGURATION_NAMESPACE = KAA_SOURCE_PREFIX + "Configuration";
    private static final String NOTIFICATION_NAMESPACE = KAA_SOURCE_PREFIX + "notification";

    @Override
    public FileData generateSdk(String buildVersion, List<BootstrapNodeInfo> bootstrapNodes,
            SdkProfileDto sdkProperties, String profileSchemaBody, String notificationSchemaBody,
            String configurationProtocolSchemaBody, String configurationBaseSchemaBody,
            byte[] defaultConfigurationData, List<EventFamilyMetadata> eventFamilies, String logSchemaBody)
            throws Exception {

        final String sdkToken = sdkProperties.getToken();
        final String defaultVerifierToken = sdkProperties.getDefaultVerifierToken();

        String sdkTemplateLocation = System.getProperty("server_home_dir") + "/" + SDK_TEMPLATE_DIR + SDK_PREFIX
                + buildVersion + ".tar.gz";

        LOG.debug("Lookup Objective C 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> objcSources = new ArrayList<>();

        if (StringUtils.isNotBlank(profileSchemaBody)) {
            LOG.debug("Generating profile schema");
            Schema profileSchema = new Schema.Parser().parse(profileSchemaBody);
            String profileClassName = PROFILE_NAMESPACE + profileSchema.getName();

            String profileCommonHeader = readResource(PROFILE_TEMPLATE_DIR + PROFILE_COMMON + _H + TEMPLATE_SUFFIX)
                    .replaceAll(PROFILE_CLASS_VAR, profileClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(profileCommonHeader,
                    KAA_ROOT + PROFILE_DIR + PROFILE_COMMON + _H));

            String profileCommonSource = readResource(PROFILE_TEMPLATE_DIR + PROFILE_COMMON + _M + TEMPLATE_SUFFIX)
                    .replaceAll(PROFILE_CLASS_VAR, profileClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(profileCommonSource,
                    KAA_ROOT + PROFILE_DIR + PROFILE_COMMON + _M));

            objcSources.addAll(generateSourcesFromSchema(profileSchema, PROFILE_GEN, PROFILE_NAMESPACE));
        }

        String logClassName = "";
        if (StringUtils.isNotBlank(logSchemaBody)) {
            LOG.debug("Generating log schema");
            Schema logSchema = new Schema.Parser().parse(logSchemaBody);
            logClassName = LOGGING_NAMESPACE + logSchema.getName();

            String logRecordHeader = readResource(LOG_TEMPLATE_DIR + LOG_RECORD + _H + TEMPLATE_SUFFIX)
                    .replaceAll(LOG_RECORD_CLASS_VAR, logClassName);

            objcSources
                    .add(CommonSdkUtil.tarEntryForSources(logRecordHeader, KAA_ROOT + LOG_DIR + LOG_RECORD + _H));

            String logRecordSource = readResource(LOG_TEMPLATE_DIR + LOG_RECORD + _M + TEMPLATE_SUFFIX)
                    .replaceAll(LOG_RECORD_CLASS_VAR, logClassName);

            objcSources
                    .add(CommonSdkUtil.tarEntryForSources(logRecordSource, KAA_ROOT + LOG_DIR + LOG_RECORD + _M));

            String logCollector = readResource(LOG_TEMPLATE_DIR + LOG_COLLECTOR_INTERFACE + TEMPLATE_SUFFIX)
                    .replaceAll(LOG_RECORD_CLASS_VAR, logClassName);

            objcSources.add(
                    CommonSdkUtil.tarEntryForSources(logCollector, KAA_ROOT + LOG_DIR + LOG_COLLECTOR_INTERFACE));

            String logCollectorImplHeader = readResource(
                    LOG_TEMPLATE_DIR + LOG_COLLECTOR_SOURCE + _H + TEMPLATE_SUFFIX).replaceAll(LOG_RECORD_CLASS_VAR,
                            logClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(logCollectorImplHeader,
                    KAA_ROOT + LOG_DIR + LOG_COLLECTOR_SOURCE + _H));

            String logCollectorImplSource = readResource(
                    LOG_TEMPLATE_DIR + LOG_COLLECTOR_SOURCE + _M + TEMPLATE_SUFFIX).replaceAll(LOG_RECORD_CLASS_VAR,
                            logClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(logCollectorImplSource,
                    KAA_ROOT + LOG_DIR + LOG_COLLECTOR_SOURCE + _M));

            objcSources.addAll(generateSourcesFromSchema(logSchema, LOG_GEN, LOGGING_NAMESPACE));
        }

        String configurationClassName = "";
        if (StringUtils.isNotBlank(configurationBaseSchemaBody)) {
            LOG.debug("Generating configuration schema");
            Schema configurationSchema = new Schema.Parser().parse(configurationBaseSchemaBody);
            configurationClassName = CONFIGURATION_NAMESPACE + configurationSchema.getName();

            String configurationCommon = readResource(
                    CONFIGURATION_TEMPLATE_DIR + CONFIGURATION_COMMON + TEMPLATE_SUFFIX)
                            .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(configurationCommon,
                    KAA_ROOT + CONFIGURATION_DIR + CONFIGURATION_COMMON));

            String cfManagerImplHeader = readResource(
                    CONFIGURATION_TEMPLATE_DIR + CONFIGURATION_MANAGER_IMPL + _H + TEMPLATE_SUFFIX)
                            .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(cfManagerImplHeader,
                    KAA_ROOT + CONFIGURATION_DIR + CONFIGURATION_MANAGER_IMPL + _H));

            String cfManagerImplSource = readResource(
                    CONFIGURATION_TEMPLATE_DIR + CONFIGURATION_MANAGER_IMPL + _M + TEMPLATE_SUFFIX)
                            .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(cfManagerImplSource,
                    KAA_ROOT + CONFIGURATION_DIR + CONFIGURATION_MANAGER_IMPL + _M));

            String cfDeserializerHeader = readResource(
                    CONFIGURATION_TEMPLATE_DIR + CONFIGURATION_DESERIALIZER + _H + TEMPLATE_SUFFIX)
                            .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(cfDeserializerHeader,
                    KAA_ROOT + CONFIGURATION_DIR + CONFIGURATION_DESERIALIZER + _H));

            String cfDeserializerSource = readResource(
                    CONFIGURATION_TEMPLATE_DIR + CONFIGURATION_DESERIALIZER + _M + TEMPLATE_SUFFIX)
                            .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(cfDeserializerSource,
                    KAA_ROOT + CONFIGURATION_DIR + CONFIGURATION_DESERIALIZER + _M));

            objcSources.addAll(
                    generateSourcesFromSchema(configurationSchema, CONFIGURATION_GEN, CONFIGURATION_NAMESPACE));
        }

        if (StringUtils.isNotBlank(notificationSchemaBody)) {
            LOG.debug("Generating notification schema");
            Schema notificationSchema = new Schema.Parser().parse(notificationSchemaBody);
            String notificationClassName = NOTIFICATION_NAMESPACE + notificationSchema.getName();

            String nfCommonHeader = readResource(
                    NOTIFICATION_TEMPLATE_DIR + NOTIFICATION_COMMON + _H + TEMPLATE_SUFFIX)
                            .replaceAll(NOTIFICATION_CLASS_VAR, notificationClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(nfCommonHeader,
                    KAA_ROOT + NOTIFICATION_DIR + NOTIFICATION_COMMON + _H));

            String nfCommonSource = readResource(
                    NOTIFICATION_TEMPLATE_DIR + NOTIFICATION_COMMON + _M + TEMPLATE_SUFFIX)
                            .replaceAll(NOTIFICATION_CLASS_VAR, notificationClassName);

            objcSources.add(CommonSdkUtil.tarEntryForSources(nfCommonSource,
                    KAA_ROOT + NOTIFICATION_DIR + NOTIFICATION_COMMON + _M));

            objcSources.addAll(
                    generateSourcesFromSchema(notificationSchema, NOTIFICATION_GEN, NOTIFICATION_NAMESPACE));
        }

        if (eventFamilies != null && !eventFamilies.isEmpty()) {
            objcSources.addAll(ObjCEventClassesGenerator.generateEventSources(eventFamilies));
        }

        String kaaClient = readResource(SDK_TEMPLATE_DIR + KAA_CLIENT + TEMPLATE_SUFFIX)
                .replaceAll(LOG_RECORD_CLASS_VAR, logClassName)
                .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

        objcSources.add(CommonSdkUtil.tarEntryForSources(kaaClient, KAA_ROOT + KAA_CLIENT));

        String baseKaaClientHeader = readResource(SDK_TEMPLATE_DIR + BASE_KAA_CLIENT + _H + TEMPLATE_SUFFIX)
                .replaceAll(LOG_RECORD_CLASS_VAR, logClassName)
                .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

        objcSources.add(CommonSdkUtil.tarEntryForSources(baseKaaClientHeader, KAA_ROOT + BASE_KAA_CLIENT + _H));
        String baseKaaClientSource = readResource(SDK_TEMPLATE_DIR + BASE_KAA_CLIENT + _M + TEMPLATE_SUFFIX)
                .replaceAll(LOG_RECORD_CLASS_VAR, logClassName)
                .replaceAll(CONFIGURATION_CLASS_VAR, configurationClassName);

        objcSources.add(CommonSdkUtil.tarEntryForSources(baseKaaClientSource, KAA_ROOT + BASE_KAA_CLIENT + _M));

        String tokenVerifier = defaultVerifierToken == null ? "nil" : "@\"" + defaultVerifierToken + "\"";

        String tokenVerifierSource = readResource(EVENT_TEMPLATE_DIR + USER_VERIFIER_CONSTANTS + TEMPLATE_SUFFIX)
                .replaceAll(DEFAULT_USER_VERIFIER_TOKEN_VAR, tokenVerifier);

        objcSources.add(CommonSdkUtil.tarEntryForSources(tokenVerifierSource,
                KAA_ROOT + EVENT_DIR + USER_VERIFIER_CONSTANTS));

        String kaaDefaultsTemplate = readResource(SDK_TEMPLATE_DIR + KAA_DEFAULTS + TEMPLATE_SUFFIX);
        objcSources.add(generateKaaDefaults(kaaDefaultsTemplate, bootstrapNodes, sdkToken,
                configurationProtocolSchemaBody, defaultConfigurationData, sdkProperties));

        for (TarEntryData entryData : objcSources) {
            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 = SDK_PREFIX + sdkProperties.getToken() + ".tar.gz";

        byte[] sdkData = sdkOutput.toByteArray();

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

    private TarEntryData generateKaaDefaults(String template, List<BootstrapNodeInfo> bootstrapNodes,
            String sdkToken, String configurationProtocolSchemaBody, byte[] defaultConfigurationData,
            SdkProfileDto profileDto) {
        LOG.debug("Generating kaa defaults");
        final int propertiesCount = 7;

        List<String> properties = new ArrayList<>(propertiesCount);
        properties.add(Version.PROJECT_VERSION);
        properties.add(Version.COMMIT_HASH);
        properties.add(sdkToken);
        properties.add(profileDto.getApplicationId());
        properties.add(Base64.encodeBase64String(defaultConfigurationData));

        properties.add(Base64.encodeBase64String(configurationProtocolSchemaBody.getBytes()));
        properties.add(CommonSdkUtil.bootstrapNodesToString(bootstrapNodes));
        String source = String.format(template, properties.toArray(new Object[propertiesCount]));
        return CommonSdkUtil.tarEntryForSources(source, KAA_ROOT + KAA_DEFAULTS);
    }

    private List<TarEntryData> generateSourcesFromSchema(Schema schema, String sourceName, String namespace) {
        LOG.debug("Generating source with name: " + sourceName);
        List<TarEntryData> tarEntries = new LinkedList<>();

        try (OutputStream headerStream = new ByteArrayOutputStream();
                OutputStream sourceStream = new ByteArrayOutputStream()) {
            Compiler compiler = new ObjectiveCCompiler(schema, sourceName, headerStream, sourceStream);
            compiler.setNamespacePrefix(namespace);
            compiler.generate();

            tarEntries.add(
                    CommonSdkUtil.tarEntryForSources(headerStream.toString(), KAA_GEN_FOLDER + sourceName + ".h"));

            tarEntries.add(
                    CommonSdkUtil.tarEntryForSources(sourceStream.toString(), KAA_GEN_FOLDER + sourceName + ".m"));

        } catch (Exception ex) {
            LOG.error("Failed to generate ObjectiveC sdk sources", ex);
        }

        return tarEntries;
    }

}