/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2013 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; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Properties; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.logging.LogChannel; import org.pentaho.di.core.logging.LogChannelInterface; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.util.SortedFileOutputStream; import org.pentaho.di.i18n.BaseMessages; /** * We use Props to store all kinds of user interactive information such as the selected colors, fonts, positions of * windows, etc. * * @author Matt * @since 15-12-2003 * */ public class Props implements Cloneable { private static Class<?> PKG = Const.class; // for i18n purposes, needed by Translator2!! private static final String STRING_USER_PREFERENCES = "User preferences"; protected static Props props; public static final String STRING_FONT_FIXED_NAME = "FontFixedName"; public static final String STRING_FONT_FIXED_SIZE = "FontFixedSize"; public static final String STRING_FONT_FIXED_STYLE = "FontFixedStyle"; public static final String STRING_FONT_DEFAULT_NAME = "FontDefaultName"; public static final String STRING_FONT_DEFAULT_SIZE = "FontDefaultSize"; public static final String STRING_FONT_DEFAULT_STYLE = "FontDefaultStyle"; public static final String STRING_FONT_GRAPH_NAME = "FontGraphName"; public static final String STRING_FONT_GRAPH_SIZE = "FontGraphSize"; public static final String STRING_FONT_GRAPH_STYLE = "FontGraphStyle"; public static final String STRING_FONT_GRID_NAME = "FontGridName"; public static final String STRING_FONT_GRID_SIZE = "FontGridSize"; public static final String STRING_FONT_GRID_STYLE = "FontGridStyle"; public static final String STRING_FONT_NOTE_NAME = "FontNoteName"; public static final String STRING_FONT_NOTE_SIZE = "FontNoteSize"; public static final String STRING_FONT_NOTE_STYLE = "FontNoteStyle"; public static final String STRING_BACKGROUND_COLOR_R = "BackgroundColorR"; public static final String STRING_BACKGROUND_COLOR_G = "BackgroundColorG"; public static final String STRING_BACKGROUND_COLOR_B = "BackgroundColorB"; public static final String STRING_GRAPH_COLOR_R = "GraphColorR"; public static final String STRING_GRAPH_COLOR_G = "GraphColorG"; public static final String STRING_GRAPH_COLOR_B = "GraphColorB"; public static final String STRING_TAB_COLOR_R = "TabColorR54"; public static final String STRING_TAB_COLOR_G = "TabColorG54"; public static final String STRING_TAB_COLOR_B = "TabColorB54"; public static final String STRING_SVG_ENABLED = "EnableSVG"; public static final String STRING_ICON_SIZE = "IconSize"; public static final String STRING_LINE_WIDTH = "LineWidth"; public static final String STRING_SHADOW_SIZE = "ShadowSize54"; public static final String STRING_LOG_LEVEL = "LogLevel"; public static final String STRING_LOG_FILTER = "LogFilter"; public static final String STRING_MIDDLE_PCT = "MiddlePct"; public static final String STRING_INDICATE_SLOW_TRANS_STEPS = "IndicateSlowTransSteps"; public static final String STRING_LAST_PREVIEW_TRANS = "LastPreviewTrans"; public static final String STRING_LAST_PREVIEW_STEP = "LastPreviewStep"; public static final String STRING_LAST_PREVIEW_SIZE = "LastPreviewSize"; public static final String STRING_MAX_UNDO = "MaxUndo"; public static final String STRING_SIZE_MAX = "SizeMax"; public static final String STRING_SIZE_X = "SizeX"; public static final String STRING_SIZE_Y = "SizeY"; public static final String STRING_SIZE_W = "SizeW"; public static final String STRING_SIZE_H = "SizeH"; public static final String STRING_SASH_W1 = "SashWeight1"; public static final String STRING_SASH_W2 = "SashWeight2"; public static final String STRING_AUTO_SAVE = "AutoSave"; public static final String STRING_SAVE_CONF = "SaveConfirmation"; public static final String STRING_AUTO_SPLIT = "AutoSplit"; public static final String STRING_AUTO_COLLAPSE_CORE_TREE = "AutoCollapseCoreObjectsTree"; public static final String STRING_USE_DB_CACHE = "UseDBCache"; public static final String STRING_OPEN_LAST_FILE = "OpenLastFile"; public static final String STRING_LAST_REPOSITORY_LOGIN = "RepositoryLastLogin"; public static final String STRING_LAST_REPOSITORY = "RepositoryLast"; public static final String STRING_ONLY_ACTIVE_STEPS = "OnlyActiveSteps"; public static final String STRING_START_SHOW_REPOSITORIES = "ShowRepositoriesAtStartup"; public static final String STRING_ANTI_ALIASING = "EnableAntiAliasing54"; public static final String STRING_SHOW_CANVAS_GRID = "ShowCanvasGrid"; public static final String STRING_SHOW_EXIT_WARNING = "ShowExitWarning"; public static final String STRING_SHOW_OS_LOOK = "ShowOSLook54"; public static final String STRING_LAST_ARGUMENT = "LastArgument"; public static final String STRING_ARGUMENT_NAME_PREFIX = "Argument "; public static final String STRING_CUSTOM_PARAMETER = "CustomParameter"; public static final String STRING_PLUGIN_HISTORY = "PluginHistory"; public static final String STRING_DEFAULT_PREVIEW_SIZE = "DefaultPreviewSize"; public static final String STRING_ONLY_USED_DB_TO_XML = "SaveOnlyUsedConnectionsToXML"; public static final String STRING_ASK_ABOUT_REPLACING_DATABASES = "AskAboutReplacingDatabases"; public static final String STRING_REPLACE_DATABASES = "ReplaceDatabases"; private static final String STRING_MAX_NR_LINES_IN_LOG = "MaxNrOfLinesInLog"; private static final String STRING_MAX_NR_LINES_IN_HISTORY = "MaxNrOfLinesInHistory"; private static final String STRING_LINES_IN_HISTORY_FETCH_SIZE = "LinesInHistoryFetchSize"; public static final String STRING_DISABLE_INITIAL_EXECUTION_HISTORY = "DisableInitialExecutionHistory"; private static final String STRING_MAX_LOG_LINE_TIMEOUT_MINUTES = "MaxLogLineTimeOutMinutes"; protected LogChannelInterface log; protected Properties properties; protected ArrayList<ObjectUsageCount> pluginHistory; protected int type; protected String filename; public static final int TYPE_PROPERTIES_EMPTY = 0; public static final int TYPE_PROPERTIES_SPOON = 1; public static final int TYPE_PROPERTIES_PAN = 2; public static final int TYPE_PROPERTIES_CHEF = 3; public static final int TYPE_PROPERTIES_KITCHEN = 4; public static final int TYPE_PROPERTIES_MENU = 5; public static final int TYPE_PROPERTIES_PLATE = 6; public static final int WIDGET_STYLE_DEFAULT = 0; public static final int WIDGET_STYLE_FIXED = 1; public static final int WIDGET_STYLE_TABLE = 2; public static final int WIDGET_STYLE_NOTEPAD = 3; public static final int WIDGET_STYLE_GRAPH = 4; public static final int WIDGET_STYLE_TAB = 5; public static final int WIDGET_STYLE_TOOLBAR = 6; /** * Initialize the properties: load from disk. * * @param display * The Display * @param t * The type of properties file. */ public static final void init( int t ) { if ( props == null ) { props = new Props( t ); } else { throw new RuntimeException( "The Properties systems settings are already initialised!" ); } } /** * Initialize the properties: load from disk. * * @param display * The Display * @param filename * the filename to use */ public static final void init( String filename ) { if ( props == null ) { props = new Props( filename ); } else { throw new RuntimeException( "The properties systems settings are already initialised!" ); } } /** * Check to see whether the Kettle properties where loaded. * * @return true if the Kettle properties where loaded. */ public static boolean isInitialized() { return props != null; } public static Props getInstance() { if ( props != null ) { return props; } throw new RuntimeException( "Properties, Kettle systems settings, not initialised!" ); } protected Props() { init(); } protected Props( int t ) { type = t; filename = getFilename(); init(); } protected void init() { createLogChannel(); properties = new Properties(); pluginHistory = new ArrayList<ObjectUsageCount>(); loadProps(); addDefaultEntries(); loadPluginHistory(); } protected Props( String filename ) { properties = new Properties(); this.type = TYPE_PROPERTIES_EMPTY; this.filename = filename; init(); } @Override public String toString() { return STRING_USER_PREFERENCES; } protected void createLogChannel() { log = new LogChannel( STRING_USER_PREFERENCES ); } public String getFilename() { String filename = ""; String directory = Const.getKettleDirectory(); switch ( type ) { case TYPE_PROPERTIES_SPOON: case TYPE_PROPERTIES_PAN: filename = directory + Const.FILE_SEPARATOR + ".spoonrc"; break; case TYPE_PROPERTIES_CHEF: case TYPE_PROPERTIES_KITCHEN: filename = directory + Const.FILE_SEPARATOR + ".chefrc"; break; case TYPE_PROPERTIES_MENU: filename = directory + Const.FILE_SEPARATOR + ".menurc"; break; case TYPE_PROPERTIES_PLATE: filename = directory + Const.FILE_SEPARATOR + ".platerc"; break; default: break; } return filename; } public String getLicenseFilename() { String directory = Const.getKettleDirectory(); String filename = directory + Const.FILE_SEPARATOR + ".licence"; // Try to create the directory... File dir = new File( directory ); if ( !dir.exists() ) { try { dir.mkdirs(); } catch ( Exception e ) { // ignore - should likely report failure to create directory } } return filename; } public boolean fileExists() { File f = new File( filename ); return f.exists(); } public void setType( int t ) { type = t; } public int getType() { return type; } public boolean loadProps() { try { FileInputStream fis = new FileInputStream( filename ); try { properties.load( fis ); } finally { try { fis.close(); } catch ( IOException ignored ) { // Ignore close exception } } } catch ( Exception e ) { return false; } return true; } protected void addDefaultEntries() { if ( !properties.containsKey( "JobDialogStyle" ) ) { properties.setProperty( "JobDialogStyle", "RESIZE,MAX,MIN" ); } } public void saveProps() { File spoonRc = new File( filename ); try { // FileOutputStream fos = new FileOutputStream(spoonRc); SortedFileOutputStream fos = new SortedFileOutputStream( spoonRc ); fos.setLogger( log ); properties.store( fos, "Kettle Properties file" ); fos.close(); log.logDetailed( BaseMessages.getString( PKG, "Spoon.Log.SaveProperties" ) ); } catch ( IOException e ) { // If saving fails this could be a known Java bug: If running Spoon on windows the spoon // config file gets created with the 'hidden' attribute set. Some Java JREs cannot open // FileOutputStreams on files with that attribute set. The user has to unset that attribute // manually. // // Note that we don't really want to throw an exception here, that would prevent usage of Kettle on read-only // systems. // if ( spoonRc.isHidden() && filename.indexOf( '\\' ) != -1 ) { // If filename contains a backslash we consider Spoon as running on Windows log.logError( BaseMessages.getString( PKG, "Spoon.Log.SavePropertiesFailedWindowsBugAttr", filename ) ); } else { // Another reason why the save failed log.logError( BaseMessages.getString( PKG, "Spoon.Log.SavePropertiesFailed" ) + e.getMessage() ); } } } public void setLogLevel( String level ) { properties.setProperty( STRING_LOG_LEVEL, level ); } public String getLogLevel() { String level = properties.getProperty( STRING_LOG_LEVEL, "Basic" ); return level; } public void setLogFilter( String filter ) { properties.setProperty( STRING_LOG_FILTER, Const.NVL( filter, "" ) ); } public String getLogFilter() { String level = properties.getProperty( STRING_LOG_FILTER, "" ); return level; } public void setUseDBCache( boolean use ) { properties.setProperty( STRING_USE_DB_CACHE, use ? "Y" : "N" ); } public boolean useDBCache() { String use = properties.getProperty( STRING_USE_DB_CACHE ); return !"N".equalsIgnoreCase( use ); } public void setLastRepository( String repname ) { properties.setProperty( STRING_LAST_REPOSITORY, repname ); } public String getLastRepository() { return properties.getProperty( STRING_LAST_REPOSITORY ); } public void setLastRepositoryLogin( String login ) { properties.setProperty( STRING_LAST_REPOSITORY_LOGIN, login ); } public String getLastRepositoryLogin() { return properties.getProperty( STRING_LAST_REPOSITORY_LOGIN ); } public void setOnlyActiveSteps( boolean only ) { properties.setProperty( STRING_ONLY_ACTIVE_STEPS, only ? "Y" : "N" ); } public boolean getOnlyActiveSteps() { String only = properties.getProperty( STRING_ONLY_ACTIVE_STEPS, "N" ); return "Y".equalsIgnoreCase( only ); // Default: show active steps. } public boolean askAboutReplacingDatabaseConnections() { String ask = properties.getProperty( STRING_ASK_ABOUT_REPLACING_DATABASES, "N" ); return "Y".equalsIgnoreCase( ask ); } public void setProperty( String propertyName, String value ) { properties.setProperty( propertyName, value ); } public String getProperty( String propertyName ) { return properties.getProperty( propertyName ); } public void setAskAboutReplacingDatabaseConnections( boolean ask ) { properties.setProperty( STRING_ASK_ABOUT_REPLACING_DATABASES, ask ? "Y" : "N" ); } /** * @param parameterName * The parameter name * @param defaultValue * The default value in case the parameter doesn't exist yet. * @return The custom parameter */ public String getCustomParameter( String parameterName, String defaultValue ) { return properties.getProperty( STRING_CUSTOM_PARAMETER + parameterName, defaultValue ); } /** * Set the custom parameter * * @param parameterName * The name of the parameter * @param value * The value to be stored in the properties file. */ public void setCustomParameter( String parameterName, String value ) { properties.setProperty( STRING_CUSTOM_PARAMETER + parameterName, value ); } public void clearCustomParameters() { Enumeration<Object> keys = properties.keys(); while ( keys.hasMoreElements() ) { String key = (String) keys.nextElement(); if ( key.startsWith( STRING_CUSTOM_PARAMETER ) ) { // Clear this one properties.remove( key ); } } } /** * Convert "argument 1" to 1 * * @param value * The value to determine the argument number for * @return The argument number */ public static final int getArgumentNumber( ValueMetaInterface value ) { if ( value != null && value.getName().startsWith( Props.STRING_ARGUMENT_NAME_PREFIX ) ) { return Const.toInt( value.getName().substring( Props.STRING_ARGUMENT_NAME_PREFIX.length() ), -1 ); } return -1; } public static final String[] convertArguments( RowMetaAndData row ) { String[] args = new String[10]; for ( int i = 0; i < row.size(); i++ ) { ValueMetaInterface valueMeta = row.getValueMeta( i ); int argNr = getArgumentNumber( valueMeta ); if ( argNr >= 0 && argNr < 10 ) { try { args[argNr] = row.getString( i, "" ); } catch ( KettleValueException e ) { args[argNr] = ""; // Should never happen } } } return args; } /** * Set the last arguments so that we can recall it the next time... * * @param args * the arguments to save */ public void setLastArguments( String[] args ) { for ( int i = 0; i < args.length; i++ ) { if ( args[i] != null ) { properties.setProperty( STRING_LAST_ARGUMENT + "_" + i, args[i] ); } } } /** * Get the last entered arguments... * * @return the last entered arguments... */ public String[] getLastArguments() { String[] args = new String[10]; for ( int i = 0; i < args.length; i++ ) { args[i] = properties.getProperty( STRING_LAST_ARGUMENT + "_" + i ); } return args; } /** * Get the list of recently used step * * @return a list of strings: the plug-in IDs */ public List<ObjectUsageCount> getPluginHistory() { return pluginHistory; } public int increasePluginHistory( String pluginID ) { for ( int i = 0; i < pluginHistory.size(); i++ ) { ObjectUsageCount usage = pluginHistory.get( i ); if ( usage.getObjectName().equalsIgnoreCase( pluginID ) ) { int uses = usage.increment(); Collections.sort( pluginHistory ); savePluginHistory(); return uses; } } addPluginHistory( pluginID, 1 ); Collections.sort( pluginHistory ); savePluginHistory(); return 1; } /* * /** Set the last plugin used in the plugin history * * @param pluginID The last plugin ID */ public void addPluginHistory( String pluginID, int uses ) { // Add at the front pluginHistory.add( new ObjectUsageCount( pluginID, uses ) ); } /** * Load the plugin history from the properties file * */ protected void loadPluginHistory() { pluginHistory = new ArrayList<ObjectUsageCount>(); int i = 0; String string = properties.getProperty( STRING_PLUGIN_HISTORY + "_" + i ); while ( string != null ) { pluginHistory.add( ObjectUsageCount.fromString( string ) ); i++; string = properties.getProperty( STRING_PLUGIN_HISTORY + "_" + i ); } Collections.sort( pluginHistory ); } private void savePluginHistory() { for ( int i = 0; i < pluginHistory.size(); i++ ) { ObjectUsageCount usage = pluginHistory.get( i ); properties.setProperty( STRING_PLUGIN_HISTORY + "_" + i, usage.toString() ); } } public boolean areOnlyUsedConnectionsSavedToXML() { String show = properties.getProperty( STRING_ONLY_USED_DB_TO_XML, "Y" ); return "Y".equalsIgnoreCase( show ); // Default: save all connections } public void setOnlyUsedConnectionsSavedToXML( boolean onlyUsedConnections ) { properties.setProperty( STRING_ONLY_USED_DB_TO_XML, onlyUsedConnections ? "Y" : "N" ); } public boolean replaceExistingDatabaseConnections() { String replace = properties.getProperty( STRING_REPLACE_DATABASES, "Y" ); return "Y".equalsIgnoreCase( replace ); } public void setReplaceDatabaseConnections( boolean replace ) { properties.setProperty( STRING_REPLACE_DATABASES, replace ? "Y" : "N" ); } public int getMaxNrLinesInLog() { String lines = properties.getProperty( STRING_MAX_NR_LINES_IN_LOG ); return Const.toInt( lines, Const.MAX_NR_LOG_LINES ); } public void setMaxNrLinesInLog( int maxNrLinesInLog ) { properties.setProperty( STRING_MAX_NR_LINES_IN_LOG, Integer.toString( maxNrLinesInLog ) ); } public int getMaxNrLinesInHistory() { String lines = properties.getProperty( STRING_MAX_NR_LINES_IN_HISTORY ); return Const.toInt( lines, Const.MAX_NR_HISTORY_LINES ); } public int getLinesInHistoryFetchSize() { String fetchSize = properties.getProperty( STRING_LINES_IN_HISTORY_FETCH_SIZE ); return Const.toInt( fetchSize, Const.HISTORY_LINES_FETCH_SIZE ); } public boolean disableInitialExecutionHistory() { String disable = properties.getProperty( STRING_DISABLE_INITIAL_EXECUTION_HISTORY, "N" ); return "Y".equalsIgnoreCase( disable ); } public void setMaxNrLinesInHistory( int maxNrLinesInHistory ) { properties.setProperty( STRING_MAX_NR_LINES_IN_HISTORY, Integer.toString( maxNrLinesInHistory ) ); } public void setLinesInHistoryFetchSize( int linesInHistoryFetchSize ) { properties.setProperty( STRING_LINES_IN_HISTORY_FETCH_SIZE, Integer.toString( linesInHistoryFetchSize ) ); } public void setDisableInitialExecutionHistory( boolean disable ) { properties.setProperty( STRING_DISABLE_INITIAL_EXECUTION_HISTORY, disable ? "Y" : "N" ); } public int getMaxLogLineTimeoutMinutes() { String minutes = properties.getProperty( STRING_MAX_LOG_LINE_TIMEOUT_MINUTES ); return Const.toInt( minutes, Const.MAX_LOG_LINE_TIMEOUT_MINUTES ); } public void setMaxLogLineTimeoutMinutes( int maxLogLineTimeoutMinutes ) { properties.setProperty( STRING_MAX_LOG_LINE_TIMEOUT_MINUTES, Integer.toString( maxLogLineTimeoutMinutes ) ); } }