/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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.pentaho.di.core.util;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.variables.Variables;
import org.pentaho.di.version.BuildVersion;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TimeZone;
public class EnvUtil {
private static Properties env = null;
/**
* Returns the properties from the users kettle home directory.
*
* @param fileName
* the relative name of the properties file in the users kettle directory.
* @return the map of properties.
*/
public static Properties readProperties( final String fileName ) throws KettleException {
if ( !new File( fileName ).exists() ) {
return readPropertiesByFullPath( Const.getKettleDirectory() + Const.FILE_SEPARATOR + fileName );
}
return readPropertiesByFullPath( fileName );
}
private static Properties readPropertiesByFullPath( final String fileName ) throws KettleException {
Properties props = new Properties();
InputStream is = null;
try {
is = new FileInputStream( fileName );
props.load( is );
} catch ( IOException ioe ) {
throw new KettleException( "Unable to read file '" + fileName + "'", ioe );
} finally {
if ( is != null ) {
try {
is.close();
} catch ( IOException e ) {
// ignore
}
}
}
return props;
}
/**
* Adds the kettle properties the the global system properties.
*
* @throws KettleException
* in case the properties file can't be read.
*/
public static void environmentInit() throws KettleException {
// Workaround for a Mac OS/X Leopard issue where getContextClassLoader() is returning
// null when run from the eclipse IDE
// http://lists.apple.com/archives/java-dev/2007/Nov/msg00385.html - DM
// Moving this hack to the first place where the NPE is triggered so all entrypoints can be debugged in Mac Eclipse
if ( Thread.currentThread().getContextClassLoader() == null ) {
Thread.currentThread().setContextClassLoader( ClassLoader.getSystemClassLoader() );
}
Map<Object, Object> kettleProperties = EnvUtil.readProperties( Const.KETTLE_PROPERTIES );
insertDefaultValues( kettleProperties );
applyKettleProperties( kettleProperties );
// Also put some default values for obscure environment variables in there...
// Place-holders if you will.
//
System.getProperties().put( Const.INTERNAL_VARIABLE_CLUSTER_SIZE, "1" );
System.getProperties().put( Const.INTERNAL_VARIABLE_SLAVE_SERVER_NUMBER, "0" );
System.getProperties().put( Const.INTERNAL_VARIABLE_SLAVE_SERVER_NAME, "slave-trans-name" );
System.getProperties().put( Const.INTERNAL_VARIABLE_STEP_COPYNR, "0" );
System.getProperties().put( Const.INTERNAL_VARIABLE_STEP_NAME, "step-name" );
System.getProperties().put( Const.INTERNAL_VARIABLE_STEP_PARTITION_ID, "partition-id" );
System.getProperties().put( Const.INTERNAL_VARIABLE_STEP_PARTITION_NR, "0" );
System.getProperties().put( Const.INTERNAL_VARIABLE_STEP_UNIQUE_COUNT, "1" );
System.getProperties().put( Const.INTERNAL_VARIABLE_STEP_UNIQUE_NUMBER, "0" );
}
private static void insertDefaultValues( Map<Object, Object> kettleProperties ) {
// If user didn't set value for USER_DIR_IS_ROOT set it to "false".
// See PDI-14522, PDI-14821
if ( !kettleProperties.containsKey( Const.VFS_USER_DIR_IS_ROOT ) ) {
kettleProperties.put( Const.VFS_USER_DIR_IS_ROOT, "false" );
}
}
public static void applyKettleProperties( Map<?, ?> kettleProperties ) {
applyKettleProperties( kettleProperties, false );
}
public static void applyKettleProperties( Map<?, ?> kettleProperties, boolean override ) {
Variables variables = new Variables();
for ( Object key : kettleProperties.keySet() ) {
String variable = (String) key;
String value = variables.environmentSubstitute( (String) kettleProperties.get( key ) );
variables.setVariable( variable, value );
}
Properties systemProperties = System.getProperties();
// Copy the data over to the system properties...
//
for ( String variable : variables.listVariables() ) {
String value = variables.getVariable( variable );
// Too many developers bump into the issue of the kettle.properties editor setting
// an empty string in the kettle.properties file...
//
if ( variable.equals( Const.KETTLE_PLUGIN_CLASSES ) || variable.equals( Const.KETTLE_PLUGIN_PACKAGES ) ) {
String jvmValue = System.getProperty( variable );
if ( !Utils.isEmpty( jvmValue ) ) {
if ( !Utils.isEmpty( value ) ) {
value += "," + jvmValue;
} else {
value = jvmValue;
}
}
} else {
if ( !override && systemProperties.containsKey( variable ) ) {
continue;
}
}
System.setProperty( variable, value );
}
}
/**
* Add a number of internal variables to the Kettle Variables at the root.
*
* @param variables
*/
public static void addInternalVariables( Properties prop ) {
// Add a bunch of internal variables
// The Kettle version
prop.put( Const.INTERNAL_VARIABLE_KETTLE_VERSION, BuildVersion.getInstance().getVersion() );
// The Kettle build version
prop.put( Const.INTERNAL_VARIABLE_KETTLE_BUILD_VERSION, BuildVersion.getInstance().getVersion() );
// The Kettle build date
prop.put( Const.INTERNAL_VARIABLE_KETTLE_BUILD_DATE, BuildVersion.getInstance().getBuildDate() );
}
/**
* Get System.getenv() in a reflection kind of way. The problem is that System.getenv() was deprecated in Java 1.4
* while reinstated in 1.5 This method will get at getenv() using reflection and will return empty properties when
* used in 1.4
*
* @return Properties containing the environment. You're not meant to change any value in the returned Properties!
*
*/
@SuppressWarnings( { "unchecked" } )
private static final Properties getEnv() {
Class<?> system = System.class;
if ( env == null ) {
Map<String, String> returnMap = null;
try {
Method method = system.getMethod( "getenv" );
returnMap = (Map<String, String>) method.invoke( system );
} catch ( Exception ex ) {
returnMap = null;
}
env = new Properties();
if ( returnMap != null ) {
// We're on a VM with getenv() defined.
ArrayList<String> list = new ArrayList<String>( returnMap.keySet() );
for ( int i = 0; i < list.size(); i++ ) {
String var = list.get( i );
String val = returnMap.get( var );
env.setProperty( var, val );
}
}
}
return env;
}
/**
* @return an array of strings, made up of all the environment variables available in the VM, format var=value. To be
* used for Runtime.exec(cmd, envp)
*/
public static final String[] getEnvironmentVariablesForRuntimeExec() {
Properties sysprops = new Properties();
sysprops.putAll( getEnv() );
sysprops.putAll( System.getProperties() );
addInternalVariables( sysprops );
String[] envp = new String[sysprops.size()];
List<Object> list = new ArrayList<Object>( sysprops.keySet() );
for ( int i = 0; i < list.size(); i++ ) {
String var = (String) list.get( i );
String val = sysprops.getProperty( var );
envp[i] = var + "=" + val;
}
return envp;
}
/**
* This method is written especially for weird JVM's like IBM's on AIX and OS/400. On these platforms, we notice that
* environment variables have an extra double quote around it... This is messing up the ability to specify things.
*
* @param key
* The key, the name of the environment variable to return
* @param def
* The default value to return in case the key can't be found
* @return The value of a System environment variable in the java virtual machine. If the key is not present, the
* variable is not defined and the default value is returned.
*/
public static final String getSystemPropertyStripQuotes( String key, String def ) {
String value = System.getProperty( key, def );
if ( value.startsWith( "\"" ) && value.endsWith( "\"" ) && value.length() > 1 ) {
return value.substring( 1, value.length() - 2 );
}
return value;
}
/**
* This method is written especially for weird JVM's like
*
* @param key
* The key, the name of the environment variable to return
* @param def
* The default value to return in case the key can't be found
* @return The value of a System environment variable in the java virtual machine. If the key is not present, the
* variable is not defined and the default value is returned.
*/
public static final String getSystemProperty( String key, String def ) {
String value = System.getProperty( key, def );
return value;
}
/**
* @param key
* The key, the name of the environment variable to return
* @return The value of a System environment variable in the java virtual machine. If the key is not present, the
* variable is not defined and null returned.
*/
public static final String getSystemProperty( String key ) {
return getSystemProperty( key, null );
}
/**
* Returns an available java.util.Locale object for the given localeCode.
*
* The localeCode code can be case insensitive, if it is available the method will find it and return it.
*
* @param localeCode
* @return java.util.Locale.
*/
public static Locale createLocale( String localeCode ) {
Locale resultLocale = null;
if ( !Utils.isEmpty( localeCode ) ) {
StringTokenizer parser = new StringTokenizer( localeCode, "_" );
if ( parser.countTokens() == 2 ) {
resultLocale = new Locale( parser.nextToken(), parser.nextToken() );
} else {
resultLocale = new Locale( localeCode );
}
}
return resultLocale;
}
public static TimeZone createTimeZone( String timeZoneId ) {
TimeZone resultTimeZone = null;
if ( !Utils.isEmpty( timeZoneId ) ) {
return TimeZone.getTimeZone( timeZoneId );
} else {
resultTimeZone = TimeZone.getDefault();
}
return resultTimeZone;
}
public static String[] getTimeZones() {
String[] timeZones = TimeZone.getAvailableIDs();
Arrays.sort( timeZones );
return timeZones;
}
public static String[] getLocaleList() {
Locale[] locales = Locale.getAvailableLocales();
String[] strings = new String[locales.length];
for ( int i = 0; i < strings.length; i++ ) {
strings[i] = locales[i].toString();
}
Arrays.sort( strings );
return strings;
}
}