<commit_before>/*
 * Copyright (c) 2016 Network New Technologies 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 com.networknt.service;

import com.networknt.config.Config;
import com.networknt.utility.ModuleRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.Introspector;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by steve on 2016-11-26.
 */
@SuppressWarnings("unchecked")
public class SingletonServiceFactory {
    private static String CONFIG_NAME = "service";
    private static Logger logger = LoggerFactory.getLogger(SingletonServiceFactory.class);

    private static Map<String, Object> serviceMap = new HashMap<>();
    // map is statically loaded to make sure there is only one instance per jvm
    static {
        ServiceConfig serviceConfig =
                (ServiceConfig) Config.getInstance().getJsonObjectConfig(CONFIG_NAME, ServiceConfig.class);
        List<Map<String, Object>> singletons = serviceConfig.getSingletons();
        //logger.debug("singletons " + singletons);
        try {
            if(singletons != null && singletons.size() > 0) {
                // for each interface or class, there might be a list of implementations or one init class::method
                for(Map<String, Object> singleton: singletons) {
                    Iterator it = singleton.entrySet().iterator();
                    if (it.hasNext()) {
                        Map.Entry<String, Object> pair = (Map.Entry)it.next();
                        String key = pair.getKey();
                        key = key.replaceAll("\\s+","");
                        Object value = pair.getValue();
                        if(value instanceof List) {
                            handleSingletonList(key, (List)value);
                        } else if(value instanceof String) {
                            handleSingletonClass(key, (String)value);
                        }
                    }
                }
            }
        } catch (Exception e) {
            // #883 during the server startup, any exception here will stops the server and there is no chance for the
            // logback to output anything to the log file or stdout/stderr. That is the reason here to printStackTrace.
            e.printStackTrace();
            logger.error("Exception:", e);
        }
        ModuleRegistry.registerModule(SingletonServiceFactory.class.getName(), Config.getInstance().getJsonMapConfigNoCache(CONFIG_NAME), null);
    }

    private static Object handleSingleImpl(List<String> interfaceClasses, List<Object> value) throws Exception {
        // only one object should be defined in value. TODO throws exception if number of object is not correct.
        Object object = value.get(0);
        if(object instanceof String) {
            Class implClass = Class.forName((String)object);
            Object obj = construct(implClass);
            for(String c: interfaceClasses) {
                serviceMap.put(c, obj);  // all interfaces share the same impl
            }
            return obj;
        }
        // map of impl class and properties.
        Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>)object;
        //logger.debug("map = " + map);
        // construct it using default construct and call all set methods with values defined in the properties
        return constructAndAddToServiceMap(interfaceClasses, map);
    }

    /**
     * @param interfaceClasses A list of the interfaces implemented by the map. (usually just one though)
     * @param map Mapping of name of concrete class to its fields (could be field name : value, or list of types to value).
     * @throws Exception
     */
    private static List<Object> constructAndAddToServiceMap(List<String> interfaceClasses, Map map) throws Exception {
        Iterator it = map.entrySet().iterator();
        List<Object> items = new ArrayList<>();
        if (it.hasNext()) {
            Map.Entry<String, Map<String, Object>> pair = (Map.Entry) it.next();
            String key = pair.getKey();
            Class implClass = Class.forName(key);
            Object mapOrList = pair.getValue();
            // at this moment, pair.getValue() has two scenarios,
            // 1. map that can be used to set properties after construct the object with reflection.
            // 2. list that can be used by matched constructor to create the instance.
            Object obj;<commit_msg>fixes #1258 add logging statements to the SingletonServiceFactory (#1259)<commit_after>/*
 * Copyright (c) 2016 Network New Technologies 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 com.networknt.service;

import com.networknt.config.Config;
import com.networknt.config.JsonMapper;
import com.networknt.utility.ModuleRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.Introspector;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by steve on 2016-11-26.
 */
@SuppressWarnings("unchecked")
public class SingletonServiceFactory {
    private static String CONFIG_NAME = "service";
    private static Logger logger = LoggerFactory.getLogger(SingletonServiceFactory.class);

    private static Map<String, Object> serviceMap = new HashMap<>();
    // map is statically loaded to make sure there is only one instance per jvm
    static {
        ServiceConfig serviceConfig =
                (ServiceConfig) Config.getInstance().getJsonObjectConfig(CONFIG_NAME, ServiceConfig.class);
        if(logger.isTraceEnabled()) logger.trace("serviceConfig = " + JsonMapper.toJson(serviceConfig));
        List<Map<String, Object>> singletons = serviceConfig.getSingletons();
        //logger.debug("singletons " + singletons);
        try {
            if(singletons != null && singletons.size() > 0) {
                // for each interface or class, there might be a list of implementations or one init class::method
                for(Map<String, Object> singleton: singletons) {
                    Iterator it = singleton.entrySet().iterator();
                    if (it.hasNext()) {
                        Map.Entry<String, Object> pair = (Map.Entry)it.next();
                        String key = pair.getKey();
                        key = key.replaceAll("\\s+","");
                        Object value = pair.getValue();
                        if(value instanceof List) {
                            handleSingletonList(key, (List)value);
                        } else if(value instanceof String) {
                            handleSingletonClass(key, (String)value);
                        }
                    }
                }
            }
        } catch (Exception e) {
            // #883 during the server startup, any exception here will stops the server and there is no chance for the
            // logback to output anything to the log file or stdout/stderr. That is the reason here to printStackTrace.
            e.printStackTrace();
            logger.error("Exception:", e);
        }
        ModuleRegistry.registerModule(SingletonServiceFactory.class.getName(), Config.getInstance().getJsonMapConfigNoCache(CONFIG_NAME), null);
    }

    private static Object handleSingleImpl(List<String> interfaceClasses, List<Object> value) throws Exception {
        // only one object should be defined in value. TODO throws exception if number of object is not correct.
        Object object = value.get(0);
        if(object instanceof String) {
            Class implClass = Class.forName((String)object);
            Object obj = construct(implClass);
            for(String c: interfaceClasses) {
                serviceMap.put(c, obj);  // all interfaces share the same impl
            }
            return obj;
        }
        // map of impl class and properties.
        Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>)object;
        //logger.debug("map = " + map);
        // construct it using default construct and call all set methods with values defined in the properties
        return constructAndAddToServiceMap(interfaceClasses, map);
    }

    /**
     * @param interfaceClasses A list of the interfaces implemented by the map. (usually just one though)
     * @param map Mapping of name of concrete class to its fields (could be field name : value, or list of types to value).
     * @throws Exception
     */
    private static List<Object> constructAndAddToServiceMap(List<String> interfaceClasses, Map map) throws Exception {
        if(logger.isTraceEnabled()) logger.trace("map = " + JsonMapper.toJson(map));
        Iterator it = map.entrySet().iterator();
        List<Object> items = new ArrayList<>();
        if (it.hasNext()) {
            Map.Entry<String, Map<String, Object>> pair = (Map.Entry) it.next();
            String key = pair.getKey();
            Class implClass = Class.forName(key);
            Object mapOrList = pair.getValue();
            // at this moment, pair.getValue() has two scenarios,
            // 1. map that can be used to set properties after construct the object with reflection.
            // 2. list that can be used by matched constructor to create the instance.
            Object obj;
{'fraction_non_alphanumeric': 0.09336890243902439, 'fraction_numerical': 0.005525914634146341, 'mean_word_length': 2.4438976377952755, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3142147', 'n_tokens_mistral': 2627, 'n_tokens_neox': 2525, 'n_words': 921}