/*
* 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
);
}
}
}