org.slc.sli.api.init.ApplicationInitializer.java Source code

Java tutorial

Introduction

Here is the source code for org.slc.sli.api.init.ApplicationInitializer.java

Source

/*
 * Copyright 2012-2013 inBloom, Inc. and its affiliates.
 *
 * 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.slc.sli.api.init;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;

import org.apache.commons.io.IOUtils;
import org.slc.sli.domain.Entity;
import org.slc.sli.domain.NeutralCriteria;
import org.slc.sli.domain.NeutralQuery;
import org.slc.sli.domain.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.PropertyPlaceholderHelper;

import com.mongodb.util.JSON;

/**
 * 
 * Bootstraps application data during API startup.
 * 
 * The application data is contained within template files in the API, e.g. applications/admin.json.
 * 
 * During startup we look for two types of properties
 * <ol>
 * <li>bootstrap.app.keys - comma-separated list identifying which apps to bootstrap</li>
 * <li>bootstrap.app.<key>.template - path within API of the template file for a given app key</li>
 * </ol>
 * 
 * Before loading the template file into mongo, it performs string substitution on the ${...} tokens.
 *
 */
@Component
public class ApplicationInitializer {

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

    @Autowired
    @Qualifier("validationRepo")
    private Repository<Entity> repository;

    @Value("file:${bootstrap.app.conf}")
    protected Resource bootstrapProperties;

    private static final String APP_RESOURCE = "application";

    @PostConstruct
    public void init() {
        if (bootstrapProperties.isReadable()) {
            Properties sliProp;
            try {
                sliProp = PropertiesLoaderUtils.loadProperties(bootstrapProperties);
                processTemplates(sliProp);
            } catch (IOException e) {
                LOG.error("Could not load boostrap properties.", e);
            }
        } else {
            LOG.warn("Could not find bootstrap properties at {}.", bootstrapProperties);
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void processTemplates(Properties sliProps) {
        if (sliProps.containsKey("bootstrap.app.keys")) {
            String[] appKeys = sliProps.getProperty("bootstrap.app.keys").split(",");
            for (String key : appKeys) {
                String templateKey = "bootstrap.app." + key + ".template";
                if (sliProps.containsKey(templateKey)) {
                    try {
                        InputStream is = null;

                        try {
                            is = new ClassPathResource(sliProps.getProperty(templateKey)).getInputStream();
                            Map appData = loadJsonFile(is, sliProps);
                            writeApplicationToMongo(appData);
                        } finally {
                            is.close();
                        }
                    } catch (IOException e) {
                        LOG.error("Problem loading JSON template.", e);
                    }
                }
            }
        }
    }

    /**
     * Determine if an app needs to be created/updated in mongo, and if so, performs the operation
     * @param appData
     */
    private void writeApplicationToMongo(Map<String, Object> appData) {
        Entity app = findExistingApp(appData);

        if (app == null) {
            LOG.info("Creating boostrap application data for {}", appData.get("name"));
            repository.create(APP_RESOURCE, appData);
        } else {
            //check if we need to update it
            long oldAppHash = InitializerUtils.checksum(app.getBody());
            long newAppHash = InitializerUtils.checksum(appData);
            if (oldAppHash != newAppHash) {
                LOG.info("Updating bootstrap application data for {}", appData.get("name"));
                app.getBody().clear();
                app.getBody().putAll(appData);
                repository.update(APP_RESOURCE, app, false);
            }
        }
    }

    /**
     * Try to look up an app in mongo first by client ID and then by app name
     * @param appData
     * @return the app entity, or null if not found
     */
    Entity findExistingApp(Map<String, Object> appData) {
        //first try to find it by client_id
        Entity app = repository.findOne(APP_RESOURCE, new NeutralQuery(
                new NeutralCriteria("client_id", NeutralCriteria.OPERATOR_EQUAL, appData.get("client_id"))));

        //next try it by app name
        if (app == null) {
            app = repository.findOne(APP_RESOURCE, new NeutralQuery(
                    new NeutralCriteria("name", NeutralCriteria.OPERATOR_EQUAL, appData.get("name"))));
        }
        return app;
    }

    /**
     * Load the JSON template and perform string substitution on the ${...} tokens
     * @param is
     * @return
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    Map<String, Object> loadJsonFile(InputStream is, Properties sliProps) throws IOException {
        StringWriter writer = new StringWriter();
        IOUtils.copy(is, writer);
        String template = writer.toString();
        PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}");
        template = helper.replacePlaceholders(template, sliProps);
        return (Map<String, Object>) JSON.parse(template);
    }
}