Java tutorial
/* * Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you 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.wso2.carbon.apimgt.hybrid.gateway.configurator; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.wso2.carbon.apimgt.hybrid.gateway.common.exception.OnPremiseGatewayException; import org.wso2.carbon.apimgt.hybrid.gateway.common.util.HttpRequestUtil; import org.wso2.carbon.apimgt.hybrid.gateway.common.util.OnPremiseGatewayConstants; import org.wso2.carbon.apimgt.hybrid.gateway.common.util.TokenUtil; import org.wso2.carbon.apimgt.hybrid.gateway.configurator.dto.MicroGatewayInitializationDTO; import org.wso2.carbon.utils.CarbonUtils; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.OptionalLong; import java.util.Properties; /** * Configurator class for Micro Gateway Configuration specific to WSO2 API Cloud */ public class Configurator { private static final Log log = LogFactory.getLog(Configurator.class); private static final String AUTHORIZATION_HEADER = "Authorization"; /** Path to Carbon Home */ private static String carbonHome; /** Path to 'repository/conf' directory */ private static String carbonConfigDirPath; /** * Main method to handle Micro Gateway Configuration * * @param args String[] */ public static void main(String[] args) { carbonHome = CarbonUtils.getCarbonHome(); if (carbonHome == null || carbonHome.isEmpty()) { log.error("Carbon Home has not been set. Startup will be cancelled."); Runtime.getRuntime().exit(1); } if (args.length < 3 || carbonHome == null || carbonHome.isEmpty()) { log.error("Required arguments are not provided. Startup will be cancelled.\n" + "Required:\n" + "\t(1) Email\n" + "\t(2) Password\n" + "\t(3) Organization Key\n"); Runtime.getRuntime().exit(1); } carbonConfigDirPath = CarbonUtils.getCarbonConfigDirPath(); //Update Gateway properties file with API cloud related configs String cloudConfigsPath = carbonHome + File.separator + ConfigConstants.RESOURCES_DIR + File.separator + ConfigConstants.CLOUD_CONFIG_DIR + File.separator + ConfigConstants.CLOUD_CONFIG_FILE_NAME; String gatewayConfigPath = carbonConfigDirPath + File.separator + OnPremiseGatewayConstants.CONFIG_FILE_NAME; updateGatewayConfigDetails(cloudConfigsPath, gatewayConfigPath); //Read Gateway properties Properties gatewayProperties = getGatewayProperties(gatewayConfigPath, args); String configToolPropertyFilePath = carbonConfigDirPath + File.separator + ConfigConstants.CONFIG_TOOL_CONFIG_FILE_NAME; //Configure api-manager.xml Properties configToolProperties = readPropertiesFromFile(configToolPropertyFilePath); setAPIMConfigurations(configToolProperties, carbonHome, gatewayProperties); //Configure registry.xml RegistryXmlConfigurator registryXmlConfigurator = new RegistryXmlConfigurator(); registryXmlConfigurator.configure(carbonConfigDirPath, gatewayProperties); //Configure log4j.properties Log4JConfigurator log4JConfigurator = new Log4JConfigurator(); log4JConfigurator.configure(carbonConfigDirPath); writeConfiguredLock(carbonHome); try { initializeOnPremGateway(gatewayProperties, carbonConfigDirPath, args); } catch (OnPremiseGatewayException | IOException e) { log.error("Error while initializing gateway.", e); Runtime.getRuntime().exit(1); } } /** * Configure api-manager.xml with given properties * * @param configToolProperties Properties * @param carbonHome String * @param gatewayProperties Properties */ protected static void setAPIMConfigurations(Properties configToolProperties, String carbonHome, Properties gatewayProperties) { Map<String, Map<String, String>> fileMap = new HashMap<>(); for (Map.Entry entry : configToolProperties.entrySet()) { String xpathKey = (String) entry.getKey(); String[] values = ((String) entry.getValue()).split("::"); String file = values[0]; String gwPropertyKey = values[1]; Map<String, String> xpathMap; if (fileMap.containsKey(file)) { xpathMap = fileMap.get(file); } else { xpathMap = new HashMap<>(); } xpathMap.put(xpathKey, gwPropertyKey); fileMap.put(file, xpathMap); } XmlConfigurator xmlConfigurator = new XmlConfigurator(); xmlConfigurator.configure(carbonHome, gatewayProperties, fileMap); } /** * Initialize the Micro Gateway in Cloud * * @param carbonConfigDirPath String * @param args String[] * @throws OnPremiseGatewayException * @throws IOException */ private static void initializeOnPremGateway(Properties gatewayProperties, String carbonConfigDirPath, String[] args) throws OnPremiseGatewayException, IOException { log.info("Initializing on-premises gateway."); String initApiUrl = gatewayProperties.getProperty(ConfigConstants.INITIALIZATION_API_URL); //Collect device details Map<String, String> deviceDetails = getDeviceDetails(); String carbonFilePath = carbonConfigDirPath + File.separator + ConfigConstants.GATEWAY_CARBON_FILE_NAME; int port = getGatewayPort(carbonFilePath); deviceDetails.put(ConfigConstants.PORT, Integer.toString(port)); String payload = getInitializationPayload(gatewayProperties, deviceDetails, args); String authHeader = createAuthHeader(args); String token = callInitializationAPI(initApiUrl, authHeader, payload); //Update token in gateway properties file String gatewayConfigPath = carbonConfigDirPath + File.separator + OnPremiseGatewayConstants.CONFIG_FILE_NAME; updateOnPremGatewayUniqueId(gatewayConfigPath, token); } /** * Update the default Micro Gateway configs with Cloud specific configs * * @param cloudConfigsPath String * @param gatewayConfigPath String */ protected static void updateGatewayConfigDetails(String cloudConfigsPath, String gatewayConfigPath) { File gatewayConfigFile = new File(gatewayConfigPath); File cloudConfigFile = new File(cloudConfigsPath); try { String cloudConfigContent = FileUtils.readFileToString(cloudConfigFile, OnPremiseGatewayConstants.DEFAULT_CHARSET); FileUtils.writeStringToFile(gatewayConfigFile, cloudConfigContent, OnPremiseGatewayConstants.DEFAULT_CHARSET); } catch (IOException e) { log.error("Error occurred while updating default Gateway configs with Cloud configs", e); Runtime.getRuntime().exit(1); } } /** * Update the Micro Gateway property file with the unique identifier * * @param gatewayConfigPath String * @param token String */ protected static void updateOnPremGatewayUniqueId(String gatewayConfigPath, String token) { File gatewayConfigFile = new File(gatewayConfigPath); try { String gatewayConfigContent = FileUtils.readFileToString(gatewayConfigFile, OnPremiseGatewayConstants.DEFAULT_CHARSET); gatewayConfigContent = gatewayConfigContent.replace(OnPremiseGatewayConstants.UNIQUE_IDENTIFIER_HOLDER, token); FileUtils.writeStringToFile(gatewayConfigFile, gatewayConfigContent, OnPremiseGatewayConstants.DEFAULT_CHARSET); } catch (IOException e) { log.error("Error occurred while updating token in Gateway property file", e); Runtime.getRuntime().exit(1); } } /** * Generate authentication header * * @param args * @return * @throws IOException */ protected static String createAuthHeader(String[] args) throws IOException { //Order of args - email, tenantDomain, password String username = args[0] + OnPremiseGatewayConstants.USERNAME_SEPARATOR + args[1]; char[] password = args[2].toCharArray(); return TokenUtil.getBasicAuthHeaderValue(username, password); } /** * Call Micro Gateway initialization API and get a unique identifier * * @param initApiUrl String * @param authHeaderValue String * @param payload String * @return token String * @throws OnPremiseGatewayException * @throws IOException */ private static String callInitializationAPI(String initApiUrl, String authHeaderValue, String payload) throws OnPremiseGatewayException, IOException { String token = ""; CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(initApiUrl); httpPost.addHeader(AUTHORIZATION_HEADER, authHeaderValue); httpPost.addHeader(OnPremiseGatewayConstants.CONTENT_TYPE_HEADER, OnPremiseGatewayConstants.CONTENT_TYPE_APPLICATION_JSON); httpPost.setEntity(new StringEntity(payload)); token = HttpRequestUtil.executeHTTPMethodWithRetry(httpClient, httpPost, OnPremiseGatewayConstants.DEFAULT_RETRY_COUNT); return token; } /** * Get a JSON String with the details required to initialize Micro Gateway * * @param gatewayProperties gateway properties * @param deviceDetails Map<String, String> * @param args String[] * @return details String * @throws IOException */ protected static String getInitializationPayload(Properties gatewayProperties, Map<String, String> deviceDetails, String[] args) throws IOException { //Create object MicroGatewayInitializationDTO microGatewayInitializationDTO = new MicroGatewayInitializationDTO(); //Order of args - email, tenantDomain, password microGatewayInitializationDTO.setTenantDomain(args[1]); microGatewayInitializationDTO.setMacAddress(deviceDetails.get(ConfigConstants.MAC_ADDRESS)); microGatewayInitializationDTO.setPort(deviceDetails.get(ConfigConstants.PORT)); microGatewayInitializationDTO.setHostName(deviceDetails.get(ConfigConstants.HOST_NAME)); // Set the GW URL, Label and Metadata microGatewayInitializationDTO .setLabel(gatewayProperties.getProperty(ConfigConstants.HYBRID_GATEWAY_LABEL_PROPERTY)); microGatewayInitializationDTO.setEnvMetadataMap(getEnvMetadataFromPropertiesFile(gatewayProperties)); microGatewayInitializationDTO.setCustomMetadataMap(getCustomMetadataFromPropertiesFile(gatewayProperties)); //Convert to JSON string ObjectMapper mapper = new ObjectMapper(); String details = mapper.writeValueAsString(microGatewayInitializationDTO); if (log.isDebugEnabled()) { log.debug("Retrieved details required to initialize on-premises gateway: " + details); } return details; } /** * Write configuration lock after the configuration is complete * * @param carbonHome String */ protected static void writeConfiguredLock(String carbonHome) { String filePath = carbonHome + File.separator + ConfigConstants.CONFIGURE_LOCK_FILE_NAME; FileOutputStream fileOutputStream = null; OutputStreamWriter outputStreamWriter = null; BufferedWriter bufferedWriter = null; try { String content = "configured"; fileOutputStream = new FileOutputStream(filePath); outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8); bufferedWriter = new BufferedWriter(outputStreamWriter); bufferedWriter.write(content); bufferedWriter.flush(); } catch (IOException e) { log.error("Error occurred while writing the lock. ", e); } finally { String msg = "Error occurred while closing the output writers for : " + filePath; try { if (fileOutputStream != null) { fileOutputStream.close(); } } catch (IOException e) { log.warn(msg, e); } try { if (outputStreamWriter != null) { outputStreamWriter.close(); } } catch (IOException e) { log.warn(msg, e); } try { if (bufferedWriter != null) { bufferedWriter.close(); } } catch (IOException e) { log.warn(msg, e); } } } /** * Read the Properties from a given property file * * @param filePath String * @return properties */ protected static Properties readPropertiesFromFile(String filePath) { InputStream inputStream = null; Properties properties = new Properties(); try { inputStream = new FileInputStream(filePath); properties.load(inputStream); } catch (IOException e) { log.error("Error occurred while reading the property file " + filePath, e); Runtime.getRuntime().exit(1); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.warn("Unable to close the Input Stream for : " + filePath, e); } } } return properties; } /** * Retrieve Gateway properties from file and add missing properties * * @param configFilePath String * @param args String[] * @return Properties */ protected static Properties getGatewayProperties(String configFilePath, String[] args) { Properties gatewayProperties = readPropertiesFromFile(configFilePath); String email = args[0]; gatewayProperties.put(ConfigConstants.EMAIL, email); String tenantDomain = args[1]; gatewayProperties.put(ConfigConstants.TENANT_DOMAIN, tenantDomain); char[] password = args[2].toCharArray(); gatewayProperties.put(ConfigConstants.PASSWORD, String.valueOf(password)); gatewayProperties.put(ConfigConstants.USERNAME, email + OnPremiseGatewayConstants.USERNAME_SEPARATOR + tenantDomain); //Following are default values for some of the configs, which will be set for a public cloud setup. These can be //overridden by adding the respective property to on-premise-gateway.properties file if (gatewayProperties.containsKey(ConfigConstants.PUBLIC_CLOUD_SETUP) && Boolean.parseBoolean(gatewayProperties.getProperty(ConfigConstants.PUBLIC_CLOUD_SETUP))) { if (!gatewayProperties.containsKey(ConfigConstants.ANALYTICS_ENABLED)) { gatewayProperties.put(ConfigConstants.ANALYTICS_ENABLED, "true"); } if (!gatewayProperties.containsKey(ConfigConstants.API_KEY_VALIDATION_CLIENT_TYPE)) { gatewayProperties.put(ConfigConstants.API_KEY_VALIDATION_CLIENT_TYPE, "WSClient"); } if (!gatewayProperties.containsKey(ConfigConstants.FILE_DATA_PUBLISHER_CLASS)) { gatewayProperties.put(ConfigConstants.FILE_DATA_PUBLISHER_CLASS, ConfigConstants.DEFAULT_FILE_DATA_PUBLISHER_CLASS); } //In a public cloud setup use StratosPublicCloudSetup as false to create tenants without a domain gatewayProperties.put(ConfigConstants.STRATOS_PUBLIC_CLOUD_SETUP, "false"); } return gatewayProperties; } /** * Retrieve host name, mac address of the device * * @return details Map<String, String> */ protected static Map<String, String> getDeviceDetails() { InetAddress ip; String hostName = ""; String macAddress = ConfigConstants.DEFAULT_MAC_ADDRESS; Map<String, String> details = new HashMap(); try { ip = InetAddress.getLocalHost(); hostName = ip.getHostName(); Enumeration<NetworkInterface> networkInterfaceEnumeration = NetworkInterface.getNetworkInterfaces(); while (networkInterfaceEnumeration.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaceEnumeration.nextElement(); Enumeration<InetAddress> enumeration = networkInterface.getInetAddresses(); for (; enumeration.hasMoreElements();) { InetAddress address = enumeration.nextElement(); if (!address.isLoopbackAddress() && !address.isLinkLocalAddress() && address.isSiteLocalAddress()) { byte[] mac = networkInterface.getHardwareAddress(); if (mac != null) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < mac.length; i++) { //Construct mac address sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ConfigConstants.DELIMITER : "")); } macAddress = sb.toString(); break; } } } } } catch (UnknownHostException | SocketException e) { log.error("Error while retrieving mac address", e); Runtime.getRuntime().exit(1); } details.put(ConfigConstants.HOST_NAME, hostName); details.put(ConfigConstants.MAC_ADDRESS, macAddress); return details; } /** * Retrieve Gateway port based on the configured port offset * * @param carbonFilePath String * @return port int */ protected static int getGatewayPort(String carbonFilePath) { int port = 0; try { File file = new File(carbonFilePath); String carbonXMLContent = FileUtils.readFileToString(file); String offsetStr = StringUtils.substringBetween(carbonXMLContent, ConfigConstants.START_OFFSET_TAG, ConfigConstants.END_OFFSET_TAG); port = ConfigConstants.GATEWAY_DEFAULT_PORT + Integer.parseInt(offsetStr); } catch (IOException e) { log.error("Error occurred while reading the carbon XML.", e); Runtime.getRuntime().exit(1); } return port; } /** * Retrieves environment metadata keys from properties file and maps with system property values accordingly * Adds last wum update date also to the metadata map * * @param gatewayProperties * @return environment metadata map */ private static Map<String, String> getEnvMetadataFromPropertiesFile(Properties gatewayProperties) { Map<String, String> properties = getAllPropertiesForPrefix(gatewayProperties, ConfigConstants.HYBRID_GATEWAY_ENV_METADATA); Map<String, String> envMetadata = new HashMap<>(); // Check whether the configured environment key has a system property value, // If true, add it to the envMetadata map for (String key : properties.keySet()) { String substringMetadataKey = key.substring(ConfigConstants.HYBRID_GATEWAY_ENV_METADATA.length()); String systemPropertyVal = System.getProperty(substringMetadataKey); if (StringUtils.isNotBlank(systemPropertyVal)) { envMetadata.put(substringMetadataKey, systemPropertyVal); } else { log.warn("Unknown environment metadata key: " + key + ". Hence Ignoring."); } } // Put carbon home and last wum update timestamp as mandatory entries to the environment data map envMetadata.put(ConfigConstants.CARBON_HOME, carbonHome); envMetadata.put(ConfigConstants.LAST_WUM_UPDATE, getLastWumUpdatedTimestamp()); if (log.isDebugEnabled()) { log.debug("System property metadata and wum updated timestamp retrieved as per the configurations is: " + envMetadata); } return envMetadata; } /** * Retrieves custom metadata keys and values from properties file * * @param gatewayProperties * @return custom metadata map */ private static Map<String, String> getCustomMetadataFromPropertiesFile(Properties gatewayProperties) { Map<String, String> allPropertiesForPrefix = getAllPropertiesForPrefix(gatewayProperties, ConfigConstants.HYBRID_GATEWAY_CUSTOM_METADATA); Map<String, String> modifiedPropertyMap = new HashMap<>(); for (String key : allPropertiesForPrefix.keySet()) { String modifiedKey = key.substring(ConfigConstants.HYBRID_GATEWAY_CUSTOM_METADATA.length()); // Check if the custom key actually has a value defined if (StringUtils.isNotBlank(allPropertiesForPrefix.get(key))) { modifiedPropertyMap.put(modifiedKey, allPropertiesForPrefix.get(key)); } } if (log.isDebugEnabled()) { log.debug("Custom metadata retrieved as per the configurations is: " + modifiedPropertyMap); } return modifiedPropertyMap; } /** * Retrieves all the properties from property file * * @param gatewayProperties * @param propertyKeyPrefix * @return all property keys and values in a map */ private static Map<String, String> getAllPropertiesForPrefix(Properties gatewayProperties, String propertyKeyPrefix) { Map<String, String> allPropertyKeyValueMap = new HashMap<>(); for (String key : gatewayProperties.stringPropertyNames()) { if (key.startsWith(propertyKeyPrefix)) { allPropertyKeyValueMap.put(key, gatewayProperties.getProperty(key)); } } if (log.isDebugEnabled()) { log.debug("Retrieving all the properties for the prefix: " + propertyKeyPrefix + ". Found key," + "value map: " + allPropertyKeyValueMap); } return allPropertyKeyValueMap; } /** * Gets the last WUM updated timestamp from the wum summary file in the 'wumDir' path * * @return last WUM updated timestamp */ private static String getLastWumUpdatedTimestamp() { String lastWumUpdateTimestamp = "-1"; Path wumDir = Paths.get(carbonHome, ConfigConstants.UPDATES_DIR, ConfigConstants.WUM_DIR); if (Files.exists(wumDir)) { OptionalLong max = OptionalLong.empty(); try { // List files in WUM directory, filter file names for numbers and get the // timestamps from file names, then get the maximum timestamp as the // the last wum updated timestamp. max = Files.list(wumDir).filter(path -> !Files.isDirectory(path)) .map(path -> path.getFileName().toString()).filter(StringUtils::isNumeric) .mapToLong(Long::parseLong).max(); } catch (IOException e) { log.error("An error occurred when retrieving last wum update time.", e); } if (max.isPresent()) { lastWumUpdateTimestamp = String.valueOf(max.getAsLong()); } else { log.warn("No WUM update information found in the file path: " + wumDir.toString()); } } else { log.warn("WUM directory not found in the file path: " + wumDir.toString()); } return lastWumUpdateTimestamp; } }