/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2017 Pentaho Corporation. All rights reserved. */ package org.pentaho.reporting.ui.datasources.jdbc.ui; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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.HypersonicDatabaseMeta; import org.pentaho.di.core.exception.KettleDatabaseException; import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeContext; import org.pentaho.reporting.ui.datasources.jdbc.DatabaseMapping; import org.pentaho.reporting.ui.datasources.jdbc.connection.DriverConnectionDefinition; import org.pentaho.reporting.ui.datasources.jdbc.connection.JdbcConnectionDefinition; import org.pentaho.reporting.ui.datasources.jdbc.connection.JndiConnectionDefinition; import org.pentaho.ui.database.Messages; import org.pentaho.ui.xul.XulComponent; import org.pentaho.ui.xul.XulDomContainer; import org.pentaho.ui.xul.XulException; import org.pentaho.ui.xul.containers.XulDialog; import org.pentaho.ui.xul.dom.Document; import org.pentaho.ui.xul.swing.SwingXulLoader; import java.awt.Window; import java.text.MessageFormat; import java.util.Iterator; import java.util.Map; import java.util.Properties; /** * Managing class for instance of Xul Commons Database Dialog. This class handles the translation between DatabaseMeta * objects and types of @{link JdbcConnectionDefinition}. * <p/> * The dialog is always modal. * * @author NBaker */ public class XulDatabaseDialog { private static final Log log = LogFactory.getLog( XulDatabaseDialog.class ); private static final String DIALOG_DEFINITION_FILE = "org/pentaho/ui/database/databasedialog.xul"; //$NON-NLS-1$ private static final String OVERLAY_DEFINITION_FILE = "org/pentaho/reporting/ui/datasources/jdbc/ui/databasedialogOverlay.xul"; //$NON-NLS-1$ private XulDialog dialog; private XulDatabaseHandler handler; private DatabaseMeta meta; private static final String HSQLDB_PREFIX = "jdbc:hsqldb:hsql://"; private static final String HSQLDB_MEM_PREFIX = "jdbc:hsqldb:mem:"; private static final String HSQLDB_LOCAL_PREFIX = "jdbc:hsqldb:."; private DesignTimeContext designTimeContext; public XulDatabaseDialog( final Window parent, final DesignTimeContext designTimeContext ) throws XulException { this.designTimeContext = designTimeContext; final SwingXulLoader loader = new SwingXulLoader(); if ( parent != null ) { loader.setOuterContext( parent ); } final XulDomContainer container = loader.loadXul( DIALOG_DEFINITION_FILE, Messages.getBundle() ); container.getDocumentRoot().addOverlay( OVERLAY_DEFINITION_FILE ); container.initialize(); handler = new XulDatabaseHandler(); container.addEventHandler( handler ); //$NON-NLS-1$ final Document documentRoot = container.getDocumentRoot(); final XulComponent root = documentRoot.getRootElement(); if ( root instanceof XulDialog ) { dialog = (XulDialog) root; dialog.setResizable( Boolean.TRUE ); } else { throw new XulException( "Error getting Xul Database Dialog root, element of type: " + root ); } } private void setData( final JdbcConnectionDefinition def ) { if ( def instanceof DriverConnectionDefinition ) { final DriverConnectionDefinition jdbcDef = (DriverConnectionDefinition) def; this.meta = new DatabaseMeta(); this.meta.setUsername( jdbcDef.getUsername() ); this.meta.setPassword( jdbcDef.getPassword() ); this.meta.setName( jdbcDef.getName() ); if ( jdbcDef.getDatabaseType() != null ) { log.debug( "Database type is known: " + jdbcDef.getDatabaseType() ); try { this.meta.setDatabaseType( jdbcDef.getDatabaseType() ); } catch ( RuntimeException re ) { // sic! } this.meta.setDBName( jdbcDef.getDatabaseName() ); this.meta.setHostname( jdbcDef.getHostName() ); this.meta.setDBPort( jdbcDef.getPort() ); this.meta.getAttributes() .setProperty( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_URL, jdbcDef.getConnectionString() ); this.meta.getAttributes() .setProperty( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_DRIVER_CLASS, jdbcDef.getDriverClass() ); } else if ( String.valueOf( jdbcDef.getConnectionString() ).startsWith( HSQLDB_MEM_PREFIX ) ) { this.meta.setDatabaseType( DatabaseMapping.getGenericInterface().getPluginId() ); this.meta.getAttributes().put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_URL, jdbcDef.getConnectionString() ); this.meta.getAttributes().put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_DRIVER_CLASS, jdbcDef.getDriverClass() ); } else if ( String.valueOf( jdbcDef.getConnectionString() ).startsWith( HSQLDB_LOCAL_PREFIX ) ) { this.meta.setDatabaseType( DatabaseMapping.getGenericInterface().getPluginId() ); this.meta.getAttributes().put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_URL, jdbcDef.getConnectionString() ); this.meta.getAttributes().put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_DRIVER_CLASS, jdbcDef.getDriverClass() ); } else { final DatabaseInterface databaseInterface = DatabaseMapping.getMappingForDriver( jdbcDef.getDriverClass() ); this.meta.setDatabaseType( databaseInterface.getPluginId() ); log.debug( "Database type is unknown, using " + databaseInterface ); try { final String pattern; if ( databaseInterface instanceof HypersonicDatabaseMeta ) { final String connectionString = jdbcDef.getConnectionString(); if ( connectionString.startsWith( HSQLDB_PREFIX ) ) { if ( connectionString.indexOf( ':', HSQLDB_PREFIX.length() ) == -1 ) { pattern = HSQLDB_PREFIX + "{0}/{2}"; } else { pattern = HSQLDB_PREFIX + "{0}:{1}/{2}"; } } else { pattern = databaseInterface.getURL( "{0}", "{1}", "{2}" ); } } else { pattern = databaseInterface.getURL( "{0}", "{1}", "{2}" ); } // knowing that most databases are written in C, we can be sure that the zero-character // is not a common value. if ( pattern != null && pattern.length() > 0 ) { final MessageFormat format = new MessageFormat( pattern ); final Object[] objects = format.parse( jdbcDef.getConnectionString() ); if ( objects[ 0 ] != null ) { this.meta.setHostname( String.valueOf( objects[ 0 ] ) ); } if ( objects[ 1 ] != null ) { this.meta.setDBPort( String.valueOf( objects[ 1 ] ) ); } if ( objects[ 2 ] != null ) { this.meta.setDBName( String.valueOf( objects[ 2 ] ) ); } } } catch ( Exception e ) { designTimeContext.error( new XulException( "Unable to parse database-URL, please report " + "your database driver to Pentaho to include it in our list of databases.", e ) ); this.meta.setDatabaseType( DatabaseMapping.getGenericInterface().getPluginId() ); this.meta.getAttributes().put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_URL, jdbcDef.getConnectionString() ); this.meta.getAttributes().put( GenericDatabaseMeta.ATRRIBUTE_CUSTOM_DRIVER_CLASS, jdbcDef.getDriverClass() ); } } final Properties properties = jdbcDef.getProperties(); final Iterator entryIterator = properties.entrySet().iterator(); while ( entryIterator.hasNext() ) { final Map.Entry entry = (Map.Entry) entryIterator.next(); final String key = (String) entry.getKey(); if ( key.startsWith( "::pentaho-reporting::" ) ) { continue; } if ( "user".equals( key ) || "password".equals( key ) ) { continue; } // This line makes the database dialog crash later. This seems to be a Swing/Xul issue. this.meta.addExtraOption( meta.getPluginId(), key, (String) entry.getValue() ); } } else if ( def instanceof JndiConnectionDefinition ) { final JndiConnectionDefinition jndiDef = (JndiConnectionDefinition) def; this.meta = new DatabaseMeta(); this.meta.setDBName( jndiDef.getJndiName() ); //JNDI name stored in DBname this.meta.setName( jndiDef.getName() ); try { if ( jndiDef.getDatabaseType() != null ) { this.meta.setDatabaseType( jndiDef.getDatabaseType() ); } } catch ( RuntimeException re ) { // even invalid values should not kill us. // sic! Kettle throws generic Exceptions. } this.meta.setAccessType( DatabaseMeta.TYPE_ACCESS_JNDI ); } else { this.meta = null; } } public JdbcConnectionDefinition open( final JdbcConnectionDefinition definition ) { setData( definition ); try { log.debug( "showing database dialog" ); if ( meta != null ) { handler.setData( meta ); } dialog.show(); //Blocks current thread log.debug( "dialog closed, getting DabaseMeta" ); if ( handler.isConfirmed() == false ) { return null; } final DatabaseMeta database = (DatabaseMeta) handler.getData(); //$NON-NLS-1$ if ( database == null ) { log.debug( "DatabaseMeta is null" ); return null; } return convertDbMeta( database ); } catch ( Exception e ) { log.error( e.getMessage(), e ); return null; } } private JdbcConnectionDefinition convertDbMeta( final DatabaseMeta meta ) throws KettleDatabaseException { if ( meta.getAccessType() == DatabaseMeta.TYPE_ACCESS_JNDI ) { return new JndiConnectionDefinition( meta.getName(), meta.getDatabaseName(), meta.getDatabaseInterface().getPluginName(), null, null ); } else { final Map<String, String> map = meta.getExtraOptions(); final Properties properties = new Properties(); final Iterator<Map.Entry<String, String>> entryIterator = map.entrySet().iterator(); while ( entryIterator.hasNext() ) { final Map.Entry<String, String> entry = entryIterator.next(); final String key = entry.getKey(); final String realKey = key.substring( meta.getPluginId().length() + 1 ); final String value = entry.getValue(); if ( DatabaseMeta.EMPTY_OPTIONS_STRING.equals( value ) ) { properties.put( realKey, "" ); } else { properties.put( realKey, value ); } } return new DriverConnectionDefinition( meta.getName(), meta.getDriverClass(), meta.getURL(), meta.getUsername(), meta.getPassword(), meta.getHostname(), meta.getDatabaseName(), meta.getDatabaseInterface().getPluginId(), meta.getDatabasePortNumberString(), properties ); } } }