/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2017 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.ui.database.event; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import org.apache.commons.lang.StringUtils; import org.eclipse.swt.widgets.Display; import org.pentaho.di.core.Const; import org.pentaho.di.core.database.BaseDatabaseMeta; import org.pentaho.di.core.database.DatabaseConnectionPoolParameter; import org.pentaho.di.core.database.DatabaseInterface; import org.pentaho.di.core.database.DatabaseMeta; import org.pentaho.di.core.database.GenericDatabaseMeta; import org.pentaho.di.core.database.MSSQLServerNativeDatabaseMeta; import org.pentaho.di.core.database.PartitionDatabaseMeta; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.logging.LogChannel; import org.pentaho.di.core.plugins.DatabasePluginType; import org.pentaho.di.core.plugins.PluginInterface; import org.pentaho.di.core.plugins.PluginRegistry; import org.pentaho.di.core.plugins.PluginTypeListener; import org.pentaho.di.core.util.Utils; import org.pentaho.ui.database.Messages; import org.pentaho.ui.util.Launch; import org.pentaho.ui.util.Launch.Status; import org.pentaho.ui.xul.XulComponent; import org.pentaho.ui.xul.XulException; import org.pentaho.ui.xul.components.XulButton; import org.pentaho.ui.xul.components.XulCheckbox; import org.pentaho.ui.xul.components.XulLabel; import org.pentaho.ui.xul.components.XulMessageBox; import org.pentaho.ui.xul.components.XulTextbox; import org.pentaho.ui.xul.components.XulTreeCell; import org.pentaho.ui.xul.containers.XulDeck; import org.pentaho.ui.xul.containers.XulDialog; import org.pentaho.ui.xul.containers.XulListbox; import org.pentaho.ui.xul.containers.XulRoot; import org.pentaho.ui.xul.containers.XulTree; import org.pentaho.ui.xul.containers.XulTreeItem; import org.pentaho.ui.xul.containers.XulTreeRow; import org.pentaho.ui.xul.containers.XulWindow; import org.pentaho.ui.xul.impl.AbstractXulEventHandler; /** * Handles all manipulation of the DatabaseMeta, data retrieval from XUL DOM and rudimentary validation. * <p/> * TODO: 2. Needs to be abstracted away from the DatabaseMeta object, so other tools in the platform can use the dialog * and their preferred database object. 3. Needs exception handling, string resourcing and logging * * @author gmoran * @created Mar 19, 2008 */ public class DataHandler extends AbstractXulEventHandler { public static final SortedMap<String, DatabaseInterface> connectionMap = new TreeMap<>(); public static final Map<String, String> connectionNametoID = new HashMap<>(); // Kettle thin related private static final String EXTRA_OPTION_WEB_APPLICATION_NAME = BaseDatabaseMeta.ATTRIBUTE_PREFIX_EXTRA_OPTION + "KettleThin.webappname"; private static final String DEFAULT_WEB_APPLICATION_NAME = "pentaho"; // The connectionMap allows us to keep track of the connection // type we are working with and the correlating database interface static { PluginRegistry registry = PluginRegistry.getInstance(); List<PluginInterface> plugins = registry.getPlugins( DatabasePluginType.class ); PluginTypeListener databaseTypeListener = new DatabaseTypeListener( registry ) { public void databaseTypeAdded( String pluginName, DatabaseInterface databaseInterface ) { connectionMap.put( pluginName, databaseInterface ); connectionNametoID.put( pluginName, databaseInterface.getPluginId() ); } public void databaseTypeRemoved( String pluginName ) { connectionMap.remove( pluginName ); connectionNametoID.remove( pluginName ); } }; registry.addPluginListener( DatabasePluginType.class, databaseTypeListener ); for ( PluginInterface plugin : plugins ) { databaseTypeListener.pluginAdded( plugin ); } } protected DatabaseMeta databaseMeta = null; private DatabaseMeta cache = new DatabaseMeta(); private XulDeck dialogDeck; private XulListbox deckOptionsBox; private XulListbox connectionBox; private XulListbox accessBox; private XulTextbox connectionNameBox; protected XulTextbox hostNameBox; protected XulTextbox databaseNameBox; protected XulTextbox portNumberBox; protected XulTextbox userNameBox; protected XulTextbox passwordBox; // Generic database specific protected XulTextbox customDriverClassBox; // Generic database specific protected XulTextbox customUrlBox; // Oracle specific protected XulTextbox dataTablespaceBox; // Oracle specific protected XulTextbox indexTablespaceBox; // MS SQL Server specific private XulTextbox serverInstanceBox; // Informix specific private XulTextbox serverNameBox; // SAP R/3 specific protected XulTextbox languageBox; // SAP R/3 specific protected XulTextbox systemNumberBox; // SAP R/3 specific protected XulTextbox clientBox; // MS SQL Server specific private XulCheckbox doubleDecimalSeparatorCheck; // MySQL specific private XulCheckbox resultStreamingCursorCheck; // Pentaho data services specific private XulTextbox webAppName; // ==== Options Panel ==== // protected XulTree optionsParameterTree; // ==== Clustering Panel ==== // private XulCheckbox clusteringCheck; protected XulTree clusterParameterTree; private XulLabel clusterParameterDescriptionLabel; // ==== Advanced Panel ==== // XulCheckbox supportBooleanDataType; XulCheckbox supportTimestampDataType; XulCheckbox quoteIdentifiersCheck; XulCheckbox lowerCaseIdentifiersCheck; XulCheckbox upperCaseIdentifiersCheck; XulCheckbox preserveReservedCaseCheck; XulCheckbox useIntegratedSecurityCheck; XulTextbox preferredSchemaName; XulTextbox sqlBox; // ==== Pooling Panel ==== // private XulLabel poolSizeLabel; private XulLabel maxPoolSizeLabel; private XulCheckbox poolingCheck; protected XulTextbox poolSizeBox; protected XulTextbox maxPoolSizeBox; private XulTextbox poolingDescription; private XulLabel poolingParameterDescriptionLabel; private XulLabel poolingDescriptionLabel; protected XulTree poolParameterTree; protected XulButton acceptButton; private XulButton cancelButton; private XulButton testButton; private XulLabel noticeLabel; public DataHandler() { } public void loadConnectionData() { // HACK: need to check if onload event was already fired. // It is called from XulDatabaseDialog from dcDialog.getSwtInstance(shell); AND dialog.show(); // Multiple calls lead to multiple numbers of database types. // Therefore we check if the connectionBox was already filled. if ( connectionBox != null ) { return; } getControls(); // Add sorted types to the listbox now. final SortedSet<String> keys = new TreeSet<String>( connectionMap.keySet() ); for ( String key : keys ) { connectionBox.addItem( key ); } PluginRegistry registry = PluginRegistry.getInstance(); registry.addPluginListener( DatabasePluginType.class, new DatabaseTypeListener( registry ) { @Override public void databaseTypeAdded( String pluginName, DatabaseInterface databaseInterface ) { if ( keys.add( pluginName ) ) { update(); } } @Override public void databaseTypeRemoved( String pluginName ) { if ( keys.remove( pluginName ) ) { update(); } } private void update() { Display.getDefault().syncExec( new Runnable() { @Override public void run() { connectionBox.removeItems(); for ( String key : keys ) { connectionBox.addItem( key ); } } } ); } } ); // HACK: Need to force height of list control, as it does not behave // well when using relative layouting connectionBox.setRows( connectionBox.getRows() ); Object key = connectionBox.getSelectedItem(); // Nothing selected yet...select first item. // TODO Implement a connection type preference, // and use that type as the default for // new databases. if ( key == null ) { key = connectionMap.firstKey(); connectionBox.setSelectedItem( key ); } // HACK: Need to force selection of first panel if ( dialogDeck != null ) { setDeckChildIndex(); } setDefaultPoolParameters(); // HACK: reDim the pooling table if ( poolParameterTree != null ) { poolParameterTree.setRows( poolParameterTree.getRows() ); } } // On Database type change public void loadAccessData() { getControls(); pushCache(); Object key = connectionBox.getSelectedItem(); // Nothing selected yet... if ( key == null ) { key = connectionMap.firstKey(); connectionBox.setSelectedItem( key ); return; } DatabaseInterface database = connectionMap.get( key ); int[] acc = database.getAccessTypeList(); Object accessKey = accessBox.getSelectedItem(); accessBox.removeItems(); // Add those access types applicable to this conneciton type for ( int value : acc ) { accessBox.addItem( DatabaseMeta.getAccessTypeDescLong( value ) ); } // HACK: Need to force height of list control, as it does not behave // well when using relative layouting accessBox.setRows( accessBox.getRows() ); // May not exist for this connection type. if ( accessKey != null ) { // This check keeps the SwtListbox from complaining about a null value accessBox.setSelectedItem( accessKey ); } // Last resort, set first as default if ( accessBox.getSelectedItem() == null ) { accessBox.setSelectedItem( DatabaseMeta.getAccessTypeDescLong( acc[0] ) ); } Map<String, String> options = null; if ( this.databaseMeta != null ) { // Apply defaults to meta if set (only current db type will be displayed) this.databaseMeta.applyDefaultOptions( database ); options = this.databaseMeta.getExtraOptions(); } else { // Otherwise clear and display defaults directly clearOptionsData(); options = database.getDefaultOptions(); } setOptionsData( options ); PartitionDatabaseMeta[] clusterInfo = null; if ( this.databaseMeta != null ) { clusterInfo = this.databaseMeta.getPartitioningInformation(); } setClusterData( clusterInfo ); popCache(); } public void editOptions( int index ) { if ( index + 1 == optionsParameterTree.getRows() ) { // editing last row add a new one below Object[][] values = optionsParameterTree.getValues(); Object[] row = values[values.length - 1]; if ( row != null && ( !StringUtils.isEmpty( (String) row[0] ) || !StringUtils.isEmpty( (String) row[1] ) ) ) { // acutally have something in current last row XulTreeRow newRow = optionsParameterTree.getRootChildren().addNewRow(); newRow.addCellText( 0, "" ); newRow.addCellText( 1, "" ); } } } public void clearOptionsData() { getControls(); if ( optionsParameterTree != null ) { optionsParameterTree.getRootChildren().removeAll(); } } public void getOptionHelp() { String message = null; DatabaseMeta database = new DatabaseMeta(); getInfo( database ); String url = database.getExtraOptionsHelpText(); if ( ( url == null ) || ( url.trim().length() == 0 ) ) { message = Messages.getString( "DataHandler.USER_NO_HELP_AVAILABLE" ); showMessage( message, false ); return; } Status status = Launch.openURL( url ); if ( status.equals( Status.Failed ) ) { message = Messages.getString( "DataHandler.USER_UNABLE_TO_LAUNCH_BROWSER", url ); showMessage( message, false ); } } public void setDeckChildIndex() { getControls(); // if pooling selected, check the parameter validity before allowing // a deck panel switch... int originalSelection = ( dialogDeck == null ? -1 : dialogDeck.getSelectedIndex() ); boolean passed = true; if ( originalSelection == 3 ) { passed = checkPoolingParameters(); } if ( passed ) { int selected = deckOptionsBox.getSelectedIndex(); if ( selected < 0 ) { selected = 0; deckOptionsBox.setSelectedIndex( 0 ); } dialogDeck.setSelectedIndex( selected ); } else { dialogDeck.setSelectedIndex( originalSelection ); deckOptionsBox.setSelectedIndex( originalSelection ); } } public void onPoolingCheck() { if ( poolingCheck != null ) { boolean dis = !poolingCheck.isChecked(); if ( poolSizeBox != null ) { poolSizeBox.setDisabled( dis ); } if ( maxPoolSizeBox != null ) { maxPoolSizeBox.setDisabled( dis ); } if ( poolSizeLabel != null ) { poolSizeLabel.setDisabled( dis ); } if ( maxPoolSizeLabel != null ) { maxPoolSizeLabel.setDisabled( dis ); } if ( poolParameterTree != null ) { poolParameterTree.setDisabled( dis ); } if ( poolingParameterDescriptionLabel != null ) { poolingParameterDescriptionLabel.setDisabled( dis ); } if ( poolingDescriptionLabel != null ) { poolingDescriptionLabel.setDisabled( dis ); } if ( poolingDescription != null ) { poolingDescription.setDisabled( dis ); } } } public void onClusterCheck() { if ( clusteringCheck != null ) { boolean dis = !clusteringCheck.isChecked(); if ( clusterParameterTree != null ) { clusterParameterTree.setDisabled( dis ); } if ( clusterParameterDescriptionLabel != null ) { clusterParameterDescriptionLabel.setDisabled( dis ); } } } public Object getData() { if ( databaseMeta == null ) { databaseMeta = new DatabaseMeta(); } if ( !windowClosed() ) { this.getInfo( databaseMeta ); } return databaseMeta; } public void setData( Object data ) { if ( data instanceof DatabaseMeta ) { databaseMeta = (DatabaseMeta) data; } setInfo( databaseMeta ); } public void pushCache() { getConnectionSpecificInfo( cache ); } public void popCache() { setConnectionSpecificInfo( cache ); } public void onCancel() { close(); } private void close() { XulComponent window = document.getElementById( "general-datasource-window" ); if ( window == null ) { // window must be root window = document.getRootElement(); } if ( window instanceof XulDialog ) { ( (XulDialog) window ).hide(); } else if ( window instanceof XulWindow ) { ( (XulWindow) window ).close(); } } private boolean windowClosed() { boolean closedWindow = true; XulComponent window = document.getElementById( "general-datasource-window" ); if ( window == null ) { // window must be root window = document.getRootElement(); } if ( window instanceof XulWindow ) { closedWindow = ( (XulWindow) window ).isClosed(); } return closedWindow; } public void onOK() { DatabaseMeta database = new DatabaseMeta(); this.getInfo( database ); boolean passed = checkPoolingParameters(); if ( !passed ) { return; } String[] remarks = database.checkParameters(); String message = ""; if ( remarks.length != 0 ) { for ( int i = 0; i < remarks.length; i++ ) { message = message.concat( "* " ).concat( remarks[i] ).concat( System.getProperty( "line.separator" ) ); } showMessage( message, false ); } else { if ( databaseMeta == null ) { databaseMeta = new DatabaseMeta(); } this.getInfo( databaseMeta ); databaseMeta.setChanged(); close(); } } public void testDatabaseConnection() { DatabaseMeta database = new DatabaseMeta(); getInfo( database ); String[] remarks = database.checkParameters(); String message = ""; if ( remarks.length != 0 ) { for ( int i = 0; i < remarks.length; i++ ) { message = message.concat( "* " ).concat( remarks[i] ).concat( System.getProperty( "line.separator" ) ); } } else { message = database.testConnection(); } showMessage( message, message.length() > 300 ); } protected void getInfo( DatabaseMeta meta ) { getControls(); if ( this.databaseMeta != null && this.databaseMeta != meta ) { meta.initializeVariablesFrom( this.databaseMeta ); } // Let's not remove any (default) options or attributes // We just need to display the correct ones for the database type below... // // In fact, let's just clear the database port... // // TODO: what about the port number? // Name: meta.setName( connectionNameBox.getValue() ); // Display Name: (PDI-12292) meta.setDisplayName( connectionNameBox.getValue() ); // Connection type: Object connection = connectionBox.getSelectedItem(); if ( connection != null ) { meta.setDatabaseType( (String) connection ); } // Access type: Object access = accessBox.getSelectedItem(); if ( access != null ) { meta.setAccessType( DatabaseMeta.getAccessType( (String) access ) ); } getConnectionSpecificInfo( meta ); // Port number: if ( portNumberBox != null ) { meta.setDBPort( portNumberBox.getValue() ); } // Option parameters: if ( optionsParameterTree != null ) { Object[][] values = optionsParameterTree.getValues(); for ( int i = 0; i < values.length; i++ ) { String parameter = (String) values[i][0]; String value = (String) values[i][1]; if ( value == null ) { value = ""; } String dbType = meta.getPluginId(); // Only if parameter are supplied, we will add to the map... if ( ( parameter != null ) && ( parameter.trim().length() > 0 ) ) { if ( value.trim().length() <= 0 ) { value = DatabaseMeta.EMPTY_OPTIONS_STRING; } meta.addExtraOption( dbType, parameter, value ); } } } // Advanced panel settings: if ( supportBooleanDataType != null ) { meta.setSupportsBooleanDataType( supportBooleanDataType.isChecked() ); } if ( supportTimestampDataType != null ) { meta.setSupportsTimestampDataType( supportTimestampDataType.isChecked() ); } if ( quoteIdentifiersCheck != null ) { meta.setQuoteAllFields( quoteIdentifiersCheck.isChecked() ); } if ( lowerCaseIdentifiersCheck != null ) { meta.setForcingIdentifiersToLowerCase( lowerCaseIdentifiersCheck.isChecked() ); } if ( upperCaseIdentifiersCheck != null ) { meta.setForcingIdentifiersToUpperCase( upperCaseIdentifiersCheck.isChecked() ); } if ( preserveReservedCaseCheck != null ) { meta.setPreserveReservedCase( preserveReservedCaseCheck.isChecked() ); } if ( preferredSchemaName != null ) { meta.setPreferredSchemaName( preferredSchemaName.getValue() ); } if ( sqlBox != null ) { meta.setConnectSQL( sqlBox.getValue() ); } // Cluster panel settings if ( clusteringCheck != null ) { meta.setPartitioned( clusteringCheck.isChecked() ); } if ( ( clusterParameterTree != null ) && ( meta.isPartitioned() ) ) { Object[][] values = clusterParameterTree.getValues(); List<PartitionDatabaseMeta> pdms = new ArrayList<PartitionDatabaseMeta>(); for ( int i = 0; i < values.length; i++ ) { String partitionId = (String) values[i][0]; if ( ( partitionId == null ) || ( partitionId.trim().length() <= 0 ) ) { continue; } String hostname = (String) values[i][1]; String port = (String) values[i][2]; String dbName = (String) values[i][3]; String username = (String) values[i][4]; String password = (String) values[i][5]; PartitionDatabaseMeta pdm = new PartitionDatabaseMeta( partitionId, hostname, port, dbName ); pdm.setUsername( username ); pdm.setPassword( password ); pdms.add( pdm ); } PartitionDatabaseMeta[] pdmArray = new PartitionDatabaseMeta[pdms.size()]; meta.setPartitioningInformation( pdms.toArray( pdmArray ) ); } if ( poolingCheck != null ) { meta.setUsingConnectionPool( poolingCheck.isChecked() ); } if ( meta.isUsingConnectionPool() ) { if ( poolSizeBox != null ) { try { int initialPoolSize = Integer.parseInt( poolSizeBox.getValue() ); meta.setInitialPoolSize( initialPoolSize ); } catch ( NumberFormatException e ) { // TODO log exception and move on ... } } if ( maxPoolSizeBox != null ) { try { int maxPoolSize = Integer.parseInt( maxPoolSizeBox.getValue() ); meta.setMaximumPoolSize( maxPoolSize ); } catch ( NumberFormatException e ) { // TODO log exception and move on ... } } if ( poolParameterTree != null ) { Object[][] values = poolParameterTree.getValues(); Properties properties = new Properties(); for ( int i = 0; i < values.length; i++ ) { boolean isChecked = false; if ( values[i][0] instanceof Boolean ) { isChecked = ( (Boolean) values[i][0] ).booleanValue(); } else { isChecked = Boolean.valueOf( (String) values[i][0] ); } if ( !isChecked ) { continue; } String parameter = (String) values[i][1]; String value = (String) values[i][2]; if ( ( parameter != null ) && ( parameter.trim().length() > 0 ) && ( value != null ) && ( value.trim().length() > 0 ) ) { properties.setProperty( parameter, value ); } } meta.setConnectionPoolingProperties( properties ); } } } private void setInfo( DatabaseMeta meta ) { if ( meta == null ) { return; } if ( meta.getAttributes().containsKey( EXTRA_OPTION_WEB_APPLICATION_NAME ) ) { meta.setDBName( (String) meta.getAttributes().get( EXTRA_OPTION_WEB_APPLICATION_NAME ) ); meta.getAttributes().remove( EXTRA_OPTION_WEB_APPLICATION_NAME ); meta.setChanged(); } getControls(); // Name: if ( connectionNameBox != null ) { connectionNameBox.setValue( meta.getDisplayName() ); } PluginRegistry registry = PluginRegistry.getInstance(); PluginInterface dInterface = registry.getPlugin( DatabasePluginType.class, meta.getPluginId() ); // Connection type: int index = ( dInterface == null ? -1 : new ArrayList<>( connectionMap.keySet() ).indexOf( dInterface.getName() ) ); if ( index >= 0 ) { connectionBox.setSelectedIndex( index ); } else { LogChannel.GENERAL.logError( "Unable to find database type " + ( dInterface == null ? "null" : dInterface.getName() ) + " in our connection map" ); } // Access type: accessBox.setSelectedItem( DatabaseMeta.getAccessTypeDescLong( meta.getAccessType() ) ); // this is broken out so we can set the cache information only when caching // connection values setConnectionSpecificInfo( meta ); loadAccessData(); // Port number: if ( portNumberBox != null ) { portNumberBox.setValue( meta.getDatabasePortNumberString() ); } // Options Parameters: setOptionsData( meta.getExtraOptions() ); // Advanced panel settings: if ( supportBooleanDataType != null ) { supportBooleanDataType.setChecked( meta.supportsBooleanDataType() ); } if ( supportTimestampDataType != null ) { supportTimestampDataType.setChecked( meta.supportsTimestampDataType() ); } if ( quoteIdentifiersCheck != null ) { quoteIdentifiersCheck.setChecked( meta.isQuoteAllFields() ); } if ( lowerCaseIdentifiersCheck != null ) { lowerCaseIdentifiersCheck.setChecked( meta.isForcingIdentifiersToLowerCase() ); } if ( upperCaseIdentifiersCheck != null ) { upperCaseIdentifiersCheck.setChecked( meta.isForcingIdentifiersToUpperCase() ); } if ( preserveReservedCaseCheck != null ) { preserveReservedCaseCheck.setChecked( meta.preserveReservedCase() ); } if ( preferredSchemaName != null ) { preferredSchemaName.setValue( Const.NVL( meta.getPreferredSchemaName(), "" ) ); } if ( sqlBox != null ) { sqlBox.setValue( meta.getConnectSQL() == null ? "" : meta.getConnectSQL() ); } // Clustering panel settings if ( clusteringCheck != null ) { clusteringCheck.setChecked( meta.isPartitioned() ); } setClusterData( meta.getPartitioningInformation() ); // Pooling panel settings if ( poolingCheck != null ) { poolingCheck.setChecked( meta.isUsingConnectionPool() ); } if ( meta.isUsingConnectionPool() ) { if ( poolSizeBox != null ) { poolSizeBox.setValue( Integer.toString( meta.getInitialPoolSize() ) ); } if ( maxPoolSizeBox != null ) { maxPoolSizeBox.setValue( Integer.toString( meta.getMaximumPoolSize() ) ); } setPoolProperties( meta.getConnectionPoolingProperties() ); } setReadOnly( meta.isReadOnly() ); setDeckChildIndex(); onPoolingCheck(); onClusterCheck(); } private void traverseDomSetReadOnly( XulComponent component, boolean readonly ) { component.setDisabled( readonly ); List<XulComponent> children = component.getChildNodes(); if ( children != null && children.size() > 0 ) { for ( XulComponent child : children ) { child.setDisabled( readonly ); traverseDomSetReadOnly( child, readonly ); } } } private void setReadOnly( boolean readonly ) { // set the readonly status of EVERYTHING! traverseDomSetReadOnly( document.getRootElement(), readonly ); if ( noticeLabel != null ) { noticeLabel.setVisible( readonly ); } if ( readonly ) { // now turn back on the cancel and test buttons if ( cancelButton != null ) { cancelButton.setDisabled( false ); } if ( testButton != null ) { testButton.setDisabled( false ); } noticeLabel.setValue( Messages.getString( "DatabaseDialog.label.ConnectionIsReadOnly" ) ); } } /** * @return the list of parameters that were enabled, but had invalid return values (null or empty) */ private boolean checkPoolingParameters() { List<String> returnList = new ArrayList<String>(); if ( poolParameterTree != null ) { Object[][] values = poolParameterTree.getValues(); for ( int i = 0; i < values.length; i++ ) { boolean isChecked = false; if ( values[i][0] instanceof Boolean ) { isChecked = ( (Boolean) values[i][0] ).booleanValue(); } else { isChecked = Boolean.valueOf( (String) values[i][0] ); } if ( !isChecked ) { continue; } String parameter = (String) values[i][1]; String value = (String) values[i][2]; if ( ( value == null ) || ( value.trim().length() <= 0 ) ) { returnList.add( parameter ); } } if ( returnList.size() > 0 ) { String parameters = System.getProperty( "line.separator" ); for ( String parameter : returnList ) { parameters = parameters.concat( parameter ).concat( System.getProperty( "line.separator" ) ); } String message = Messages.getString( "DataHandler.USER_INVALID_PARAMETERS" ).concat( parameters ); showMessage( message, false ); } } return returnList.size() <= 0; } private void setPoolProperties( Properties properties ) { if ( poolParameterTree != null ) { Object[][] values = poolParameterTree.getValues(); for ( int i = 0; i < values.length; i++ ) { String parameter = (String) values[i][1]; boolean isChecked = properties.containsKey( parameter ); if ( !isChecked ) { continue; } XulTreeItem item = poolParameterTree.getRootChildren().getItem( i ); item.getRow().addCellText( 0, "true" ); // checks the checkbox String value = properties.getProperty( parameter ); item.getRow().addCellText( 2, value ); } } } public void restoreDefaults() { if ( poolParameterTree != null ) { for ( int i = 0; i < poolParameterTree.getRootChildren().getItemCount(); i++ ) { XulTreeItem item = poolParameterTree.getRootChildren().getItem( i ); String parameterName = item.getRow().getCell( 1 ).getLabel(); String defaultValue = DatabaseConnectionPoolParameter .findParameter( parameterName, BaseDatabaseMeta.poolingParameters ).getDefaultValue(); if ( ( defaultValue == null ) || ( defaultValue.trim().length() <= 0 ) ) { continue; } item.getRow().addCellText( 2, defaultValue ); } } } private void setDefaultPoolParameters() { if ( poolParameterTree != null ) { for ( DatabaseConnectionPoolParameter parameter : BaseDatabaseMeta.poolingParameters ) { XulTreeRow row = poolParameterTree.getRootChildren().addNewRow(); row.addCellText( 0, "false" ); row.addCellText( 1, parameter.getParameter() ); row.addCellText( 2, parameter.getDefaultValue() ); } } } private void removeTypedOptions( Map<String, String> extraOptions ) { List<Integer> removeList = new ArrayList<Integer>(); Object[][] values = optionsParameterTree.getValues(); for ( int i = 0; i < values.length; i++ ) { String parameter = (String) values[i][0]; // See if it's defined Iterator<String> keys = extraOptions.keySet().iterator(); if ( extraOptions.keySet().size() > 0 ) { while ( keys.hasNext() ) { String param = keys.next(); String parameterKey = param.substring( param.indexOf( '.' ) + 1 ); if ( parameter.equals( parameterKey ) || "".equals( parameter ) ) { // match, remove it if not already in the list if ( !removeList.contains( i ) ) { removeList.add( i ); } } } } else if ( "".equals( parameter ) ) { if ( !removeList.contains( i ) ) { removeList.add( i ); } } } for ( int i = removeList.size() - 1; i >= 0; i-- ) { optionsParameterTree.getRootChildren().removeItem( removeList.get( i ) ); } } private void setOptionsData( Map<String, String> extraOptions ) { if ( optionsParameterTree == null ) { return; } if ( extraOptions != null ) { removeTypedOptions( extraOptions ); Iterator<String> keys = extraOptions.keySet().iterator(); Object connection = connectionBox.getSelectedItem(); String currentType = null; if ( connection != null ) { currentType = connectionMap.get( connection.toString() ).getPluginId(); } while ( keys.hasNext() ) { String parameter = keys.next(); String value = extraOptions.get( parameter ); if ( ( value == null ) || ( value.trim().length() <= 0 ) || ( value.equals( DatabaseMeta.EMPTY_OPTIONS_STRING ) ) ) { value = ""; } // If the parameter starts with a database type code we show it in the options, otherwise we don't. // For example MySQL.defaultFetchSize // int dotIndex = parameter.indexOf( '.' ); if ( dotIndex >= 0 ) { String parameterOption = parameter.substring( dotIndex + 1 ); String databaseTypeString = parameter.substring( 0, dotIndex ); String databaseType = databaseTypeString; if ( currentType != null && currentType.equals( databaseType ) ) { XulTreeRow row = optionsParameterTree.getRootChildren().addNewRow(); row.addCellText( 0, parameterOption ); row.addCellText( 1, value ); } } } } // Have at least 5 option rows, with at least one blank int numToAdd = 5; int numSet = optionsParameterTree.getRootChildren().getItemCount(); if ( numSet < numToAdd ) { numToAdd -= numSet; } else { numToAdd = 1; } while ( numToAdd-- > 0 ) { XulTreeRow row = optionsParameterTree.getRootChildren().addNewRow(); row.addCellText( 0, "" ); // easy way of putting new cells in the row row.addCellText( 1, "" ); } } private void setClusterData( PartitionDatabaseMeta[] clusterInformation ) { if ( clusterParameterTree == null ) { // there's nothing to do return; } clusterParameterTree.getRootChildren().removeAll(); if ( ( clusterInformation != null ) && ( clusterParameterTree != null ) ) { for ( int i = 0; i < clusterInformation.length; i++ ) { PartitionDatabaseMeta meta = clusterInformation[i]; XulTreeRow row = clusterParameterTree.getRootChildren().addNewRow(); row.addCellText( 0, Const.NVL( meta.getPartitionId(), "" ) ); row.addCellText( 1, Const.NVL( meta.getHostname(), "" ) ); row.addCellText( 2, Const.NVL( meta.getPort(), "" ) ); row.addCellText( 3, Const.NVL( meta.getDatabaseName(), "" ) ); row.addCellText( 4, Const.NVL( meta.getUsername(), "" ) ); row.addCellText( 5, Const.NVL( meta.getPassword(), "" ) ); } } // Add 5 blank rows if none are already there, otherwise, just add one. int numToAdd = 5; /* * if(clusterInformation != null && clusterInformation.length > 0){ numToAdd = 1; } */ while ( numToAdd-- > 0 ) { XulTreeRow row = clusterParameterTree.getRootChildren().addNewRow(); row.addCellText( 0, "" ); // easy way of putting new cells in the row row.addCellText( 1, "" ); row.addCellText( 2, "" ); row.addCellText( 3, "" ); row.addCellText( 4, "" ); row.addCellText( 5, "" ); } } public void poolingRowChange( int idx ) { if ( idx != -1 ) { if ( idx >= BaseDatabaseMeta.poolingParameters.length ) { idx = BaseDatabaseMeta.poolingParameters.length - 1; } if ( idx < 0 ) { idx = 0; } poolingDescription.setValue( BaseDatabaseMeta.poolingParameters[idx].getDescription() ); XulTreeRow row = poolParameterTree.getRootChildren().getItem( idx ).getRow(); if ( row.getSelectedColumnIndex() == 2 ) { row.addCellText( 0, "true" ); } } } private void getConnectionSpecificInfo( DatabaseMeta meta ) { // Hostname: if ( hostNameBox != null ) { meta.setHostname( hostNameBox.getValue() ); } // Database name: if ( databaseNameBox != null ) { meta.setDBName( databaseNameBox.getValue() ); } // Username: if ( userNameBox != null ) { meta.setUsername( userNameBox.getValue() ); } // Password: if ( passwordBox != null ) { meta.setPassword( passwordBox.getValue() ); } // if(this.portNumberBox != null){ // meta.setDBPort(portNumberBox.getValue()); // } // Streaming result cursor: if ( resultStreamingCursorCheck != null ) { meta.setStreamingResults( resultStreamingCursorCheck.isChecked() ); } // Data tablespace: if ( dataTablespaceBox != null ) { meta.setDataTablespace( dataTablespaceBox.getValue() ); } // Index tablespace if ( indexTablespaceBox != null ) { meta.setIndexTablespace( indexTablespaceBox.getValue() ); } // The SQL Server instance name overrides the option. // Empty doesn't clear the option, we have mercy. if ( serverInstanceBox != null ) { meta.setSQLServerInstance( serverInstanceBox.getValue() ); if ( optionsParameterTree != null && optionsParameterTree.getRootChildren() != null ) { for ( int i = 0; i < optionsParameterTree.getRootChildren().getItemCount(); i++ ) { XulTreeItem potRow = optionsParameterTree.getRootChildren().getItem( i ); if ( potRow != null && potRow.getRow() != null ) { XulTreeCell cell = potRow.getRow().getCell( 0 ); XulTreeCell cell2 = potRow.getRow().getCell( 1 ); if ( cell != null && cell.getLabel() != null && cell.getLabel().equals( "instance" ) ) { cell2.setLabel( serverInstanceBox.getValue() ); if ( serverInstanceBox.getValue().trim().length() == 0 ) { cell.setLabel( "" ); } } } } } } // SQL Server double decimal separator if ( doubleDecimalSeparatorCheck != null ) { meta.setUsingDoubleDecimalAsSchemaTableSeparator( doubleDecimalSeparatorCheck.isChecked() ); } // SAP Attributes... if ( languageBox != null ) { meta.getAttributes().put( "SAPLanguage", languageBox.getValue() ); } if ( systemNumberBox != null ) { meta.getAttributes().put( "SAPSystemNumber", systemNumberBox.getValue() ); } if ( clientBox != null ) { meta.getAttributes().put( "SAPClient", clientBox.getValue() ); } // Generic settings... if ( customUrlBox != null ) { meta.getAttributes().put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_URL, customUrlBox.getValue() ); } if ( customDriverClassBox != null ) { meta .getAttributes() .put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_DRIVER_CLASS, customDriverClassBox.getValue() ); } // Server Name: (Informix) if ( serverNameBox != null ) { meta.setServername( serverNameBox.getValue() ); } // Microsoft SQL Server Use Integrated Security if ( useIntegratedSecurityCheck != null ) { Boolean useIntegratedSecurity = useIntegratedSecurityCheck.isChecked(); meta.getAttributes().put( MSSQLServerNativeDatabaseMeta.ATTRIBUTE_USE_INTEGRATED_SECURITY, useIntegratedSecurity != null ? useIntegratedSecurity.toString() : "false" ); } if ( webAppName != null ) { meta.setDBName( webAppName.getValue() ); } } private void setConnectionSpecificInfo( DatabaseMeta meta ) { getControls(); if ( hostNameBox != null ) { hostNameBox.setValue( meta.getHostname() ); } // Database name: if ( databaseNameBox != null ) { databaseNameBox.setValue( meta.getDatabaseName() ); } // Username: if ( userNameBox != null ) { userNameBox.setValue( meta.getUsername() ); } // Password: if ( passwordBox != null ) { passwordBox.setValue( meta.getPassword() ); } // if(this.portNumberBox != null){ // this.portNumberBox.setValue(meta.getDatabasePortNumberString()); // } // Streaming result cursor: if ( resultStreamingCursorCheck != null ) { resultStreamingCursorCheck.setChecked( meta.isStreamingResults() ); } // Data tablespace: if ( dataTablespaceBox != null ) { dataTablespaceBox.setValue( meta.getDataTablespace() ); } // Index tablespace if ( indexTablespaceBox != null ) { indexTablespaceBox.setValue( meta.getIndexTablespace() ); } if ( serverInstanceBox != null ) { serverInstanceBox.setValue( meta.getSQLServerInstance() ); } // SQL Server double decimal separator if ( doubleDecimalSeparatorCheck != null ) { doubleDecimalSeparatorCheck.setChecked( meta.isUsingDoubleDecimalAsSchemaTableSeparator() ); } // SAP Attributes... if ( languageBox != null ) { languageBox.setValue( meta.getAttributes().getProperty( "SAPLanguage" ) ); } if ( systemNumberBox != null ) { systemNumberBox.setValue( meta.getAttributes().getProperty( "SAPSystemNumber" ) ); } if ( clientBox != null ) { clientBox.setValue( meta.getAttributes().getProperty( "SAPClient" ) ); } // Generic settings... if ( customUrlBox != null ) { customUrlBox.setValue( meta.getAttributes().getProperty( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_URL ) ); } if ( customDriverClassBox != null ) { customDriverClassBox.setValue( meta.getAttributes().getProperty( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_DRIVER_CLASS ) ); } // Server Name: (Informix) if ( serverNameBox != null ) { serverNameBox.setValue( meta.getServername() ); } // Microsoft SQL Server Use Integrated Security if ( useIntegratedSecurityCheck != null ) { Object value = meta.getAttributes().get( MSSQLServerNativeDatabaseMeta.ATTRIBUTE_USE_INTEGRATED_SECURITY ); if ( value != null && value instanceof String ) { String useIntegratedSecurity = (String) value; useIntegratedSecurityCheck.setChecked( Boolean.parseBoolean( useIntegratedSecurity ) ); } else { useIntegratedSecurityCheck.setChecked( false ); } } if ( webAppName != null ) { // Insert default value only for new connection, allowing it to be empty in case of editing existing one if ( databaseMeta == null || Utils.isEmpty( databaseMeta.getDisplayName() ) ) { webAppName.setValue( DEFAULT_WEB_APPLICATION_NAME ); } else { webAppName.setValue( meta.getDatabaseName() ); } } } protected void getControls() { // Not all of these controls are created at the same time.. that's OK, for now, just check // each one for null before using. dialogDeck = (XulDeck) document.getElementById( "dialog-panel-deck" ); deckOptionsBox = (XulListbox) document.getElementById( "deck-options-list" ); connectionBox = (XulListbox) document.getElementById( "connection-type-list" ); accessBox = (XulListbox) document.getElementById( "access-type-list" ); connectionNameBox = (XulTextbox) document.getElementById( "connection-name-text" ); hostNameBox = (XulTextbox) document.getElementById( "server-host-name-text" ); databaseNameBox = (XulTextbox) document.getElementById( "database-name-text" ); portNumberBox = (XulTextbox) document.getElementById( "port-number-text" ); userNameBox = (XulTextbox) document.getElementById( "username-text" ); passwordBox = (XulTextbox) document.getElementById( "password-text" ); dataTablespaceBox = (XulTextbox) document.getElementById( "data-tablespace-text" ); indexTablespaceBox = (XulTextbox) document.getElementById( "index-tablespace-text" ); serverInstanceBox = (XulTextbox) document.getElementById( "instance-text" ); serverNameBox = (XulTextbox) document.getElementById( "server-name-text" ); customUrlBox = (XulTextbox) document.getElementById( "custom-url-text" ); customDriverClassBox = (XulTextbox) document.getElementById( "custom-driver-class-text" ); languageBox = (XulTextbox) document.getElementById( "language-text" ); systemNumberBox = (XulTextbox) document.getElementById( "system-number-text" ); clientBox = (XulTextbox) document.getElementById( "client-text" ); doubleDecimalSeparatorCheck = (XulCheckbox) document.getElementById( "decimal-separator-check" ); resultStreamingCursorCheck = (XulCheckbox) document.getElementById( "result-streaming-check" ); webAppName = (XulTextbox) document.getElementById( "web-application-name-text" ); poolingCheck = (XulCheckbox) document.getElementById( "use-pool-check" ); clusteringCheck = (XulCheckbox) document.getElementById( "use-cluster-check" ); clusterParameterDescriptionLabel = (XulLabel) document.getElementById( "cluster-parameter-description-label" ); poolSizeLabel = (XulLabel) document.getElementById( "pool-size-label" ); poolSizeBox = (XulTextbox) document.getElementById( "pool-size-text" ); maxPoolSizeLabel = (XulLabel) document.getElementById( "max-pool-size-label" ); maxPoolSizeBox = (XulTextbox) document.getElementById( "max-pool-size-text" ); poolParameterTree = (XulTree) document.getElementById( "pool-parameter-tree" ); clusterParameterTree = (XulTree) document.getElementById( "cluster-parameter-tree" ); optionsParameterTree = (XulTree) document.getElementById( "options-parameter-tree" ); poolingDescription = (XulTextbox) document.getElementById( "pooling-description" ); poolingParameterDescriptionLabel = (XulLabel) document.getElementById( "pool-parameter-description-label" ); poolingDescriptionLabel = (XulLabel) document.getElementById( "pooling-description-label" ); supportBooleanDataType = (XulCheckbox) document.getElementById( "supports-boolean-data-type" ); supportTimestampDataType = (XulCheckbox) document.getElementById( "supports-timestamp-data-type" ); quoteIdentifiersCheck = (XulCheckbox) document.getElementById( "quote-identifiers-check" ); lowerCaseIdentifiersCheck = (XulCheckbox) document.getElementById( "force-lower-case-check" ); upperCaseIdentifiersCheck = (XulCheckbox) document.getElementById( "force-upper-case-check" ); preserveReservedCaseCheck = (XulCheckbox) document.getElementById( "preserve-reserved-case" ); preferredSchemaName = (XulTextbox) document.getElementById( "preferred-schema-name-text" ); sqlBox = (XulTextbox) document.getElementById( "sql-text" ); useIntegratedSecurityCheck = (XulCheckbox) document.getElementById( "use-integrated-security-check" ); acceptButton = (XulButton) document.getElementById( "general-datasource-window_accept" ); cancelButton = (XulButton) document.getElementById( "general-datasource-window_cancel" ); testButton = (XulButton) document.getElementById( "test-button" ); noticeLabel = (XulLabel) document.getElementById( "notice-label" ); if ( portNumberBox != null && serverInstanceBox != null ) { if ( Boolean.parseBoolean( serverInstanceBox.getAttributeValue( "shouldDisablePortIfPopulated" ) ) ) { serverInstanceBox.addPropertyChangeListener( new PropertyChangeListener() { @Override public void propertyChange( PropertyChangeEvent evt ) { if ( "value".equals( evt.getPropertyName() ) ) { disablePortIfInstancePopulated(); } } } ); } } } public void disablePortIfInstancePopulated() { String serverInstance = serverInstanceBox.getValue(); if ( serverInstance != null && serverInstance.length() > 0 ) { portNumberBox.setDisabled( true ); } else { portNumberBox.setDisabled( false ); } } protected void showMessage( String message, boolean scroll ) { try { XulMessageBox box = (XulMessageBox) document.createElement( "messagebox" ); box.setMessage( message ); box.setModalParent( ( (XulRoot) document.getElementById( "general-datasource-window" ) ).getRootObject() ); if ( scroll ) { box.setScrollable( true ); box.setWidth( 500 ); box.setHeight( 400 ); } box.open(); } catch ( XulException e ) { System.out.println( "Error creating messagebox " + e.getMessage() ); } } public void handleUseSecurityCheckbox() { if ( useIntegratedSecurityCheck != null ) { if ( useIntegratedSecurityCheck.isChecked() ) { userNameBox.setDisabled( true ); passwordBox.setDisabled( true ); } else { userNameBox.setDisabled( false ); passwordBox.setDisabled( false ); } } } protected abstract static class DatabaseTypeListener implements PluginTypeListener { private final PluginRegistry registry; public DatabaseTypeListener( PluginRegistry registry ) { this.registry = registry; } @Override public void pluginAdded( Object serviceObject ) { PluginInterface plugin = (PluginInterface) serviceObject; String pluginName = plugin.getName(); try { DatabaseInterface databaseInterface = (DatabaseInterface) registry.loadClass( plugin ); databaseInterface.setPluginId( plugin.getIds()[0] ); databaseInterface.setName( pluginName ); databaseTypeAdded( pluginName, databaseInterface ); } catch ( KettleException e ) { Throwable t = e; if ( e.getCause() != null ) { t = e.getCause(); } System.out.println( "Could not create connection entry for " + pluginName + ". " + t.getClass().getName() ); LogChannel.GENERAL.logError( "Could not create connection entry for " + pluginName + ". " + t.getClass().getName() ); } } public abstract void databaseTypeAdded( String pluginName, DatabaseInterface databaseInterface ); @Override public void pluginRemoved( Object serviceObject ) { PluginInterface plugin = (PluginInterface) serviceObject; String pluginName = plugin.getName(); databaseTypeRemoved( pluginName ); } public abstract void databaseTypeRemoved( String pluginName ); @Override public void pluginChanged( Object serviceObject ) { pluginRemoved( serviceObject ); pluginAdded( serviceObject ); } } }