com.ibm.watson.app.common.services.impl.BluemixServicesBinder.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.watson.app.common.services.impl.BluemixServicesBinder.java

Source

/* Copyright IBM Corp. 2015
 *
 * 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 com.ibm.watson.app.common.services.impl;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.inject.Binder;
import com.google.inject.multibindings.Multibinder;
import com.ibm.watson.app.common.services.BluemixConfiguredService;
import com.ibm.watson.app.common.services.nlclassifier.NLClassifierService;
import com.ibm.watson.app.common.services.nlclassifier.impl.NLClassifierServiceImpl;
import com.ibm.watson.app.common.services.sqldb.SqlDBService;
import com.ibm.watson.app.common.services.sqldb.impl.SqlDBServiceImpl;
import com.ibm.watson.app.common.util.rest.MessageKey;

public final class BluemixServicesBinder {
    private static final Logger logger = LogManager.getLogger();

    private static final String VCAP_SERVICES_ENV_VAR = "VCAP_SERVICES";
    private static final String VCAP_SERVICES_FILE = "vcap.services";

    // This map holds all of the info needed for Bluemix service's configuration
    static final ConcurrentMap<String, BluemixServiceInfo<? extends BluemixConfiguredService>> CONFIGURED_SERVICES = new ConcurrentHashMap<>();
    static {
        // List currently available services, additional ones can be registered later
        addBluemixServiceConfig("natural_language_classifier", NLClassifierService.class,
                NLClassifierServiceImpl.class);
        addBluemixServiceConfig("sqldb", SqlDBService.class, SqlDBServiceImpl.class);
    }

    // A list of all available bluemix services
    static final ConcurrentMap<Class<? extends BluemixConfiguredService>, List<BluemixConfiguredService>> AVAILABLE_SERVICES = new ConcurrentHashMap<>();

    public static <T extends BluemixConfiguredService> int bindAll(Binder binder, Class<T> clazz) {

        if (AVAILABLE_SERVICES.isEmpty()) {
            registerBluemixServices();
        }

        // No matter what, we must create the multibinder, even if there are no available instances
        // Otherwise, Guice will fail to find a binding for Set<T>, when ultimately we want an empty one
        final Multibinder<T> mbinder = Multibinder.newSetBinder(binder, clazz);

        if (!AVAILABLE_SERVICES.containsKey(clazz)) {
            return 0;
        }

        @SuppressWarnings("unchecked")
        List<T> services = (List<T>) AVAILABLE_SERVICES.get(clazz);
        for (T service : services) {
            mbinder.addBinding().toInstance(service);
        }
        return services.size();
    }

    public static <T extends BluemixConfiguredService> int bind(Binder binder, Class<T> clazz) {

        if (AVAILABLE_SERVICES.isEmpty()) {
            registerBluemixServices();
        }

        @SuppressWarnings("unchecked")
        List<T> services = (List<T>) AVAILABLE_SERVICES.get(clazz);
        if (services == null || services.isEmpty()) {
            return 0;
        }
        if (services.size() > 1) {
            throw new IllegalStateException(MessageKey.AQWEGA04002E_multiple_instances_of_service_found_1
                    .getMessage(clazz.getSimpleName()).getFormattedMessage());
        }

        binder.bind(clazz).toInstance(services.get(0));
        return 1;
    }

    private static boolean registerBaseBluemixServices() {
        String jsonString = null;

        // First, check the system property and try to get a valid JSON file
        String jsonFilename = System.getProperty(VCAP_SERVICES_FILE);
        if (jsonFilename != null) {
            try {
                jsonString = FileUtils.readFileToString(new File(jsonFilename), StandardCharsets.UTF_8);
            } catch (IOException e) {
                logger.catching(Level.DEBUG, e);
            }
            if (jsonString != null && !jsonString.isEmpty()) {
                logger.info(MessageKey.AQWEGA10000I_parsing_vcap_services_json_conf_from_file_1
                        .getMessage(jsonFilename));
                parseBluemixConfig(jsonString);
                return true;
            }
        }

        // No system property for file, see if the the VCAP_SERVICES system property itself is set
        jsonString = System.getProperty(VCAP_SERVICES_ENV_VAR);
        if (jsonString != null && !jsonString.isEmpty()) {
            logger.info(MessageKey.AQWEGA10001I_parsing_vcap_services_json_from_system_property.getMessage());
            parseBluemixConfig(jsonString);
            return true;
        }

        // No system property, now try the environment
        jsonString = System.getenv(VCAP_SERVICES_ENV_VAR);
        if (jsonString != null && !jsonString.isEmpty()) {
            logger.info(MessageKey.AQWEGA10001I_parsing_vcap_services_json_from_system_property.getMessage());
            parseBluemixConfig(jsonString);
            return true;
        }
        return false;
    }

    private synchronized static void registerBluemixServices() {

        // If already parsed, return
        if (!AVAILABLE_SERVICES.isEmpty()) {
            return;
        }
        boolean baseServicesFound = registerBaseBluemixServices();
        String jsonString = null;

        jsonString = System.getenv("SERVICES_OVERRIDE");
        if (jsonString != null && !jsonString.isEmpty()) {
            logger.info(
                    MessageKey.AQWEGA10002I_parsing_vcap_services_json_from_system_property_override.getMessage());
            parseBluemixConfig(jsonString);
            return;
        }

        if (!baseServicesFound) {
            logger.warn(MessageKey.AQWEGA12000W_no_vcap_services_conf_available.getMessage());
        }

    }

    static void parseBluemixConfig(String jsonString) {
        final BluemixServicesConfigurationParser parser = new BluemixServicesConfigurationParser() {
            @Override
            protected void register(BluemixServiceInfo<? extends BluemixConfiguredService> service,
                    BluemixConfiguredService serviceImpl) {
                // To not break backwards compatability, 
                // override this method here and register the implementations with this class instead of the ServicesFactory
                List<BluemixConfiguredService> services = AVAILABLE_SERVICES.get(service.iface);
                if (services == null) {
                    final List<BluemixConfiguredService> newServicesList = new CopyOnWriteArrayList<>();
                    services = AVAILABLE_SERVICES.putIfAbsent(service.iface, newServicesList);
                    if (services == null) {
                        services = newServicesList;
                    }
                }
                services.add(serviceImpl);
            }
        };

        if (logger.isDebugEnabled()) {
            logger.debug("Parsing service configuration: " + jsonString);
        }

        parser.parseAndRegisterServices(jsonString);
    }

    // Service configuration 

    /**
     * Add a configuration for a given Bluemix service.
     * @param iface
     * @param impl
     * @param serviceNames
     */
    public static <T extends BluemixConfiguredService> void addBluemixServiceConfig(String serviceName,
            Class<T> iface, Class<? extends T> impl) {
        @SuppressWarnings("unchecked")
        BluemixServiceInfo<T> service = (BluemixServiceInfo<T>) CONFIGURED_SERVICES.get(serviceName);
        if (service == null) {
            service = new BluemixServiceInfo<T>(serviceName, iface, impl);
            CONFIGURED_SERVICES.putIfAbsent(serviceName, service);
        }
    }

    protected static BluemixServiceInfo<? extends BluemixConfiguredService> getBluemixServiceByName(String key) {
        for (BluemixServiceInfo<? extends BluemixConfiguredService> service : CONFIGURED_SERVICES.values()) {
            if (key.equals(service.name)) {
                return service;
            }
        }
        // Couldn't find a match on exact name. Try by prefix
        for (BluemixServiceInfo<? extends BluemixConfiguredService> service : CONFIGURED_SERVICES.values()) {
            if (key.startsWith(service.name)) {
                return service;
            }
        }
        return null;
    }

    // Bluemix only service registration (done during deserialization)
    protected static <T extends BluemixConfiguredService> void registerBluemixService(Class<T> iface,
            BluemixConfiguredService impl) {
        Objects.requireNonNull(impl);

        List<BluemixConfiguredService> services = AVAILABLE_SERVICES.get(iface);
        if (services == null) {
            final List<BluemixConfiguredService> newServicesList = new CopyOnWriteArrayList<>();
            services = AVAILABLE_SERVICES.putIfAbsent(iface, newServicesList);
            if (services == null) {
                services = newServicesList;
            }
        }
        services.add(impl);
    }
}