/*
* 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) 2009 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.reporting.ui.datasources.jdbc.ui;
import java.awt.Window;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
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.libraries.base.util.StringUtils;
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;
/**
* 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)
{
final String username = meta.getUsername();
final String password = meta.getPassword();
return new JndiConnectionDefinition(meta.getName(),
meta.getDatabaseName(),
meta.getDatabaseInterface().getPluginName(),
StringUtils.isEmpty(username) ? null : username,
StringUtils.isEmpty(password) ? null : password);
}
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
);
}
}
}