package com.eactive.eai.message.manager; import java.io.FileInputStream; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.StringUtils; import com.eactive.eai.common.exception.ExceptionUtil; import com.eactive.eai.common.lifecycle.Lifecycle; import com.eactive.eai.common.lifecycle.LifecycleException; import com.eactive.eai.common.lifecycle.LifecycleListener; import com.eactive.eai.common.lifecycle.LifecycleSupport; import com.eactive.eai.common.util.Logger; import com.eactive.eai.message.EncodingVar; import com.eactive.eai.message.StandardMessage; import com.eactive.eai.message.StandardMessageUtil; import com.eactive.eai.message.filter.MessageFilter; import com.eactive.eai.message.mapper.MessageMapper; import com.eactive.eai.message.parser.StandardReader; import com.eactive.eai.message.service.InterfaceMapper; import com.eactive.eai.message.service.StandardMessageCoordinator; public class StandardMessageManager implements Lifecycle { static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); private static StandardMessageManager instance; private boolean started; private LifecycleSupport lifecycle = new LifecycleSupport(this); /** # standard-message-config.properties # layout : StandardMessage layout definition layout.file.type=CSV layout.file.path=./resources/standard-layout.csv # mapper : StandardMessage's fields getter/setter interface mapper.class=com.eactive.eai.message.service.DefaultInterfaceMapper mapper.definition=./resources/standard-message-mapping-config.properties # reader : parsing input data to StandardMessage reader.JSON=com.eactive.eai.message.parser.JsonReader reader.XML=com.eactive.eai.message.parser.XmlReader reader.FLAT=com.eactive.eai.message.parser.FlatReader */ public static String STANDARD_MESSAGE_CONFIG = "standard.message.config"; private String DEFAULT_CONFIG_PATH = "./standard-message-config.properties"; private String DEFAULT_CONFIG_FILE = "standard-message-config.properties"; private String LAYOUT_FILE_TYPE = "layout.file.type"; private String LAYOUT_FILE_PATH = "layout.file.path"; private String LAYOUT_FILTER = "layout.filter.FLAT"; private String MAPPER_CLASS = "mapper.class"; private String MAPPER_DEFINITION = "mapper.definition"; private String READER_PREFIX = "reader."; private String ENCODE_FLAT = "encode.flat"; private String ENCODE_XML = "encode.xml"; private String ENCODE_JSON = "encode.json"; private String COORDINATOR_CLASS = "message.coordinator.class"; private String DEFAULT_COORDINATOR_CLASS = "com.eactive.eai.message.service.DefaultStandardMessageCoordinator"; private String REQUEST_PROCESSOR_CLASS = "requestProcessor.class"; private StandardMessage standardMessage = null; private InterfaceMapper mapper; private StandardMessageCoordinator messageCoordinator; private ConcurrentHashMap readerMap = null; private Map versionMap = new HashMap<>();; private String VERSION_LAYOUT_NAME = "version.layout.name"; private String VERSION_LAYOUT_FILTER = "version.layout.filter.FLAT"; private String VERSION_MAPPER = "version.mapper"; private String VERSION_MAPPING_DEFINATION = "version.mapper.definition"; private MessageMapper versionMapper; private StandardMessage versionMessage = null; private String versionLayoutName = null; public Map getVersionMap() { return versionMap; } public void setVersionMap(Map versionMap) { this.versionMap = versionMap; } public String getVersionLayoutName() { return versionLayoutName; } public void setVersionLayoutName(String versionLayoutName) { this.versionLayoutName = versionLayoutName; } public synchronized StandardMessage getVersionMessage() { if(versionMessage == null) return null; try { return (StandardMessage) versionMessage.clone(); } catch (CloneNotSupportedException e) { return SerializationUtils.clone(standardMessage); } } private StandardMessageManager() { readerMap = new ConcurrentHashMap<>(); } public static StandardMessageManager getInstance() { return LazyHolder.INSTANCE; } private static class LazyHolder { private static final StandardMessageManager INSTANCE = new StandardMessageManager(); } public void start() throws LifecycleException { if (started) throw new LifecycleException("RECEAICMM201"); lifecycle.fireLifecycleEvent(STARTING_EVENT, this); try { init(); } catch(Exception e) { throw new LifecycleException(ExceptionUtil.getErrorCode(e,"RECEAICMM202")); } started = true; lifecycle.fireLifecycleEvent(STARTED_EVENT, this); } public void stop() throws LifecycleException { if (!started) throw new LifecycleException("RECEAICMM203"); lifecycle.fireLifecycleEvent(STOPING_EVENT, this); readerMap.clear(); readerMap = null; started = false; lifecycle.fireLifecycleEvent(STOPPED_EVENT, this); } public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } public LifecycleListener[] findLifecycleListeners() { return lifecycle.findLifecycleListeners(); } public void removeLifecycleListener(LifecycleListener listener) { lifecycle.removeLifecycleListener(listener); } public boolean isStarted() { return this.started; } public synchronized StandardMessage getStandardMessage() { try { return (StandardMessage) standardMessage.clone(); } catch (CloneNotSupportedException e) { return SerializationUtils.clone(standardMessage); } } public MessageMapper getVersionMapper() { return versionMapper; } public void setVersionMapper(MessageMapper versionMapper) { this.versionMapper = versionMapper; } public InterfaceMapper getMapper() { return mapper; } public void setMapper(InterfaceMapper mapper) { this.mapper = mapper; } public StandardMessageCoordinator getMessageCoordinator() { return messageCoordinator; } public void setMessageCoordinator(StandardMessageCoordinator messageCoordinator) { this.messageCoordinator = messageCoordinator; } public synchronized void reload() throws Exception { if(logger.isWarn()) { logger.warn("StandardMessageManager] reload all started ..."); } init(); if(logger.isWarn()) { logger.warn("StandardMessageManager] reload all finished ..."); } } @SuppressWarnings("rawtypes") private void initReaderFactory(Properties config) { Class cl = null; StandardReader reader = null; Set keys = config.keySet(); for(Object key: keys) { String keyStr = (String)key; if(keyStr.startsWith(READER_PREFIX)) { String name = keyStr.substring(READER_PREFIX.length()); String readerClassName = config.getProperty(keyStr); try { cl = Class.forName(readerClassName); reader = (StandardReader)cl.newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { if(logger.isWarn()) logger.warn(String.format("Invalid class path : %s %s", keyStr, readerClassName), e); continue; } readerMap.put(name, reader); if(logger.isWarn()) logger.warn(String.format("# Add reader : %s %s", keyStr, readerClassName)); } } } public StandardReader getReader(String messageType) { return readerMap.get(messageType); } @SuppressWarnings("rawtypes") public void init() throws Exception { try { Properties config = loadConfigFile(); logger.debug(config.toString()); String encodeFlat = config.getProperty(ENCODE_FLAT, "euc-kr"); String encodeXml = config.getProperty(ENCODE_XML, "utf-8"); String encodeJson = config.getProperty(ENCODE_JSON, encodeXml); String requestProcessorClass = config.getProperty(REQUEST_PROCESSOR_CLASS); if(logger.isDebug()) { logger.debug( "{} : {}", ENCODE_FLAT, encodeFlat ); logger.debug( "{} : {}", ENCODE_XML, encodeXml ); logger.debug( "{} : {}", ENCODE_JSON, encodeJson ); logger.debug( "{} : {}", REQUEST_PROCESSOR_CLASS, requestProcessorClass ); } EncodingVar.flatEncoding = encodeFlat; EncodingVar.xmlEncoding = encodeXml; EncodingVar.jsonEncoding = encodeJson; StandardMessageUtil.requestProcessorClass = requestProcessorClass; String layoutFileType = config.getProperty(LAYOUT_FILE_TYPE, "CSV"); String path = config.getProperty(LAYOUT_FILE_PATH); if(logger.isDebug()) { // logger.debug( String.format("%s : %s", LAYOUT_FILE_TYPE, layoutFileType) ); // logger.debug( String.format("%s : %s", LAYOUT_FILE_PATH, path) ); logger.debug( "{} : {}", LAYOUT_FILE_TYPE, layoutFileType ); logger.debug( "{} : {}", LAYOUT_FILE_PATH, path ); } if("CSV".contentEquals(layoutFileType.toUpperCase())) { standardMessage = StandardMessageUtil.generateMessageFromCsvFile(path); } else if(StringUtils.startsWithIgnoreCase(layoutFileType, "DB")) { String layout = StringUtils.substring(layoutFileType, 3); standardMessage = StandardMessageUtil.generateMessageFromDb(StringUtils.trimToNull(layout)); } else { standardMessage = StandardMessageUtil.generateMessageFromCsvFile(path); } if(standardMessage != null) { standardMessage.setReadPosition(0); } String flatFilterClass = config.getProperty(LAYOUT_FILTER); logger.debug("{} : {}", LAYOUT_FILTER, flatFilterClass); String versionFlatFilterClass = config.getProperty(VERSION_LAYOUT_FILTER); logger.debug("{} : {}", VERSION_LAYOUT_FILTER, versionFlatFilterClass); if(StringUtils.isNotBlank(flatFilterClass)) { try { Class cl = Class.forName(flatFilterClass); MessageFilter flatFilter = (MessageFilter)cl.newInstance(); standardMessage.setFlatFilter(flatFilter); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { if(logger.isWarn()) logger.warn(String.format("configure %s : %s", LAYOUT_FILTER, flatFilterClass), e); } } String coordinatorClass = config.getProperty(COORDINATOR_CLASS, DEFAULT_COORDINATOR_CLASS); logger.debug("{} : {}", COORDINATOR_CLASS, coordinatorClass); try { Class cl = Class.forName(coordinatorClass); this.messageCoordinator = (StandardMessageCoordinator) cl.newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { if (logger.isWarn()) logger.warn(String.format("configure %s : %s", COORDINATOR_CLASS, config.getProperty(COORDINATOR_CLASS)), e); } String mapperClass = config.getProperty(MAPPER_CLASS); String mapperFilePath = config.getProperty(MAPPER_DEFINITION); if(logger.isDebug()) logger.debug( String.format("%s : %s", MAPPER_CLASS, mapperClass) ); if(logger.isDebug()) logger.debug( String.format("%s : %s", MAPPER_DEFINITION, mapperFilePath) ); if(mapperClass == null) { throw new Exception("StandardMessageManager | config error, not found config : " + MAPPER_CLASS); } if(mapperFilePath == null) { throw new Exception("StandardMessageManager | config error, not found config : " + MAPPER_DEFINITION); } initReaderFactory(config); Class cl = null; try { cl = Class.forName(mapperClass); mapper = (InterfaceMapper)cl.newInstance(); Properties mapperMapPropties = loadConfigFileProperties(mapperFilePath); HashMap map = propertyToMap(mapperMapPropties); mapper.initPathMap(map); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); return; } if(logger.isDebug()) logger.debug("mapperClass : {}", mapper.getClass().getCanonicalName()); String versionMapperClass = config.getProperty(VERSION_MAPPER); if(StringUtils.isEmpty(versionMapperClass)) { versionMapperClass = "com.eactive.eai.message.mapper.DefaultMessageMapper"; } if(logger.isDebug()) logger.debug("version.mapper : {}", versionMapperClass); try { Class clazz = Class.forName(versionMapperClass); versionMapper = (MessageMapper)clazz.newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); return; } if(logger.isDebug()) logger.debug("version.mapper class : {}", versionMapper.getClass().getCanonicalName()); String versionMapFilePath = config.getProperty(VERSION_MAPPING_DEFINATION); if(logger.isDebug()) logger.debug( String.format("%s : %s", VERSION_MAPPING_DEFINATION, versionMapFilePath) ); if(versionMapFilePath != null) { Properties versionMapPropties = loadConfigFileProperties(versionMapFilePath); versionMap = propertyToMap(versionMapPropties); } if(logger.isDebug()) logger.debug("versionMap size ="+ versionMap.size() ); versionLayoutName = config.getProperty(VERSION_LAYOUT_NAME); if(logger.isDebug()) logger.debug( String.format("%s : %s", VERSION_LAYOUT_NAME, versionLayoutName) ); if(versionLayoutName != null) { versionMessage = StandardMessageUtil.generateMessageFromDb(versionLayoutName); versionMessage.setReadPosition(0); if(StringUtils.isNotBlank(versionFlatFilterClass)) { try { Class vcl = Class.forName(versionFlatFilterClass); MessageFilter versionFlatFilter = (MessageFilter)vcl.newInstance(); versionMessage.setFlatFilter(versionFlatFilter); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { if(logger.isWarn()) logger.warn(String.format("configure %s : %s", VERSION_LAYOUT_FILTER, versionFlatFilterClass), e); } } } } catch(Exception ex) { ex.printStackTrace(); logger.error("init failed", ex); } } private HashMap propertyToMap(Properties prop) { HashMap retMap = new HashMap<>(); for (Map.Entry entry : prop.entrySet()) { retMap.put(String.valueOf(entry.getKey()).trim(), String.valueOf(entry.getValue()).trim()); } return retMap; } private Properties loadConfigFile() throws Exception { String configFile = getConfigFile(); return loadConfigFileProperties(configFile); } private Properties loadConfigFileProperties(String configFile) throws Exception { try { return loadFileProperties(configFile); } catch(Exception e) { // logger.warn("StandardMessageManager | loadFileProperties failed. - " + configFile, e); logger.warn("StandardMessageManager | loadFileProperties failed. - " + configFile +", " +e.getMessage()); } // try classpath file return loadClassPathProperties(configFile); } private Properties loadFileProperties(String filePath) throws Exception { logger.debug("configFile : ", filePath); Properties p = new Properties(); try(InputStream in = new FileInputStream(filePath)) { p.load(in); return p; } catch(Exception e) { throw new Exception("StandardMessageManager | Cannot load config file. - " + filePath, e); } } private Properties loadClassPathProperties(String filePath) throws Exception { Properties p = new Properties(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); logger.warn("StandardMessageManager | ClassLoader : load. - "+ filePath); try( InputStream in = classLoader.getResourceAsStream(filePath) ) { p.load(in); return p; } catch(Exception e) { if(logger.isWarn()) { // logger.warn("StandardMessageManager | ClassLoader : Cannot load config file. - " + filePath, e); logger.warn("StandardMessageManager | ClassLoader : Cannot load config file. - " + filePath +", " +e.getMessage()); } } logger.warn("StandardMessageManager | ClassLoader : load default - "+ DEFAULT_CONFIG_FILE); try( InputStream in = classLoader.getResourceAsStream(DEFAULT_CONFIG_FILE) ) { p.load(in); logger.warn("StandardMessageManager | ClassLoader : load default success - "+ DEFAULT_CONFIG_FILE); return p; } catch(Exception e) { logger.error("StandardMessageManager | ClassLoader : Cannot load default config file. - " + DEFAULT_CONFIG_FILE, e); throw new Exception("StandardMessageManager | ClassLoader : Cannot load default config file. - " + DEFAULT_CONFIG_FILE, e); } } private String getConfigFile() { String configFilePath = System.getProperty(STANDARD_MESSAGE_CONFIG); if (StringUtils.isEmpty(configFilePath)) { return DEFAULT_CONFIG_PATH; } else { return configFilePath; } } }