# HG changeset patch # Parent 7a11090fca0c96f1092dd83d1efbc42009582392 #197408: Enable editting of connection properties for db connections diff --git a/db/apichanges.xml b/db/apichanges.xml --- a/db/apichanges.xml +++ b/db/apichanges.xml @@ -110,6 +110,25 @@ + + Enable editting of connection properties for db connections + + + + + + + Users can specify connection properties in the UI now. + The properties can be retrieved using new method + DatabaseConnection.getConnectionProperties(). There is also a + new factory method DatabaseConnection.create() that accepts an + object with additional connection properties. + + + + + + Allow specify display name of the DatabaseConnection diff --git a/db/libsrc/org/netbeans/lib/ddl/DBConnection.java b/db/libsrc/org/netbeans/lib/ddl/DBConnection.java --- a/db/libsrc/org/netbeans/lib/ddl/DBConnection.java +++ b/db/libsrc/org/netbeans/lib/ddl/DBConnection.java @@ -45,6 +45,7 @@ package org.netbeans.lib.ddl; import java.sql.Connection; +import java.util.Properties; /** * Connection information. @@ -143,4 +144,22 @@ * driver or database does not exist or is inaccessible. */ public Connection createJDBCConnection() throws DDLException; + + /** + * Set additional (besides "user" and "password") properties of the + * connection. Use {@link #setUser(String)} and {@link #setPassword(String)} + * for setting user and password. + * + * @param connectionProperties Additional connection properties. + */ + public void setConnectionProperties(Properties connectionProperties); + + /** + * Get additional (besides "user" and "password") connection properties. Use + * {@link #getUser()} and {@link #getPassword()} to get user and password. + * + * @return Object containing additional connection properties, or null if it + * is not available. + */ + public Properties getConnectionProperties(); } diff --git a/db/nbproject/project.properties b/db/nbproject/project.properties --- a/db/nbproject/project.properties +++ b/db/nbproject/project.properties @@ -45,7 +45,7 @@ javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=1.52.0 +spec.version.base=1.53 extra.module.files=modules/ext/ddl.jar diff --git a/db/src/org/netbeans/api/db/explorer/DatabaseConnection.java b/db/src/org/netbeans/api/db/explorer/DatabaseConnection.java --- a/db/src/org/netbeans/api/db/explorer/DatabaseConnection.java +++ b/db/src/org/netbeans/api/db/explorer/DatabaseConnection.java @@ -45,6 +45,8 @@ package org.netbeans.api.db.explorer; import java.sql.Connection; +import java.sql.Driver; +import java.util.Properties; import javax.swing.SwingUtilities; import org.netbeans.modules.db.explorer.ConnectionList; import org.netbeans.modules.db.explorer.DatabaseConnectionAccessor; @@ -148,6 +150,33 @@ public static DatabaseConnection create(JDBCDriver driver, String databaseURL, String user, String schema, String password, boolean rememberPassword, String displayName) { + return create(driver, databaseURL, user, schema, password, + rememberPassword, displayName, null); + } + + /** + * Creates a new DatabaseConnection instance. + * + * @param driver the JDBC driver the new connection uses; cannot be null. + * @param databaseURL the URL of the database to connect to; cannot be null. + * @param user the username. + * @param schema the schema to use, or null for the default schema + * @param password the password. + * @param rememberPassword whether to remember the password for the current + * session. + * @param displayName the display name of the connection as it shows under + * the Databases node + * @param connectionProperties Additional connection properties, see + * {@link #getConnectionProperties()}. + * + * @return the new instance. + * + * @since 1.53 + * @throws NullPointerException if driver or database are null. + */ + public static DatabaseConnection create(JDBCDriver driver, String databaseURL, + String user, String schema, String password, boolean rememberPassword, + String displayName, Properties connectionProperties) { if (driver == null || databaseURL == null) { throw new NullPointerException(); } @@ -160,6 +189,7 @@ conn.setPassword(password); conn.setRememberPassword(rememberPassword); conn.setDisplayName(displayName); + conn.setConnectionProperties(connectionProperties); return conn.getDatabaseConnection(); } @@ -237,6 +267,26 @@ } /** + * Returns additional connection properties for the connection. + *

+ * The properties can be set by the user and may be used e.g. in + * {@link Driver#connect(String, Properties)}. Note that properties "user" + * and "password" are not included in this object, use {@link #getUser()} + * and {@link #getPassword()}. Additional properties are usually needed to + * configure some database-specific connection options (e.g. charset). + *

+ *

+ * Changes made to returned object will have no effect (copy of internal + * properties is returned). + *

+ * @return the connection properties (maybe null) + * @since db/1.53 + */ + public Properties getConnectionProperties() { + return delegate.getConnectionProperties(); + } + + /** * Returns the {@link java.sql.Connection} instance which encapsulates * the physical connection to the database if this database connection * is connected. Note that "connected" here means "connected using the diff --git a/db/src/org/netbeans/api/db/explorer/node/Bundle.properties b/db/src/org/netbeans/api/db/explorer/node/Bundle.properties --- a/db/src/org/netbeans/api/db/explorer/node/Bundle.properties +++ b/db/src/org/netbeans/api/db/explorer/node/Bundle.properties @@ -142,6 +142,8 @@ ForeignColumnDescription=Column KeySeq=Keyseq KeySeqDescription=Keyseq +ConnectionProperties=Connection properties +ConnectionPropertiesDescription=Connection properties # Booleans diff --git a/db/src/org/netbeans/modules/db/explorer/DatabaseConnection.java b/db/src/org/netbeans/modules/db/explorer/DatabaseConnection.java --- a/db/src/org/netbeans/modules/db/explorer/DatabaseConnection.java +++ b/db/src/org/netbeans/modules/db/explorer/DatabaseConnection.java @@ -156,6 +156,10 @@ */ private MetadataModel metadataModel = null; + /** Properties for connection + */ + private Properties connectionProperties = new Properties(); + /** * The API DatabaseConnection (delegates to this instance) */ @@ -173,6 +177,7 @@ public static final String PROP_DRIVERNAME = "drivername"; //NOI18N public static final String PROP_NAME = "name"; //NOI18N public static final String PROP_DISPLAY_NAME = "displayName"; //NOI18N + public static final String PROP_CONNECTIONPROPERTIES = "connectionProperties"; public static final String DRIVER_CLASS_NET = "org.apache.derby.jdbc.ClientDriver"; // NOI18N public static final int DERBY_UNICODE_ERROR_CODE = 20000; private OpenConnectionInterface openConnection = null; @@ -210,22 +215,34 @@ * @param password User password */ public DatabaseConnection(String driver, String database, String user, String password) { - this(driver, null, database, null, user, password, null); + this(driver, null, database, null, user, password, null, null); } public DatabaseConnection(String driver, String driverName, String database, String theschema, String user, String password) { - this(driver, driverName, database, theschema, user, password, null); + this(driver, driverName, database, theschema, user, password, null, null); } public DatabaseConnection(String driver, String driverName, String database, String theschema, String user) { - this(driver, driverName, database, theschema, user, null, null); + this(driver, driverName, database, theschema, user, null, null, null); + } + + public DatabaseConnection(String driver, String driverName, String database, + String theschema, String user, Properties connectionProperties) { + this(driver, driverName, database, theschema, user, null, null, connectionProperties); } public DatabaseConnection(String driver, String driverName, String database, String theschema, String user, String password, Boolean rememberPassword) { + this(driver, driverName, database, theschema, user, password, + rememberPassword, null); + } + + public DatabaseConnection(String driver, String driverName, String database, + String theschema, String user, String password, + Boolean rememberPassword, Properties connectionProperties) { this(); drv = driver; drvname = driverName; @@ -235,6 +252,7 @@ rpwd = rememberPassword == null ? null : Boolean.valueOf(rememberPassword); schema = theschema; name = getName(); + setConnectionProperties(connectionProperties); } public JDBCDriver findJDBCDriver() { @@ -548,6 +566,22 @@ } } + @Override + public Properties getConnectionProperties() { + return (Properties) connectionProperties.clone(); + } + + @Override + public void setConnectionProperties(Properties connectionProperties) { + Properties old = this.connectionProperties; + if (connectionProperties == null) { + this.connectionProperties = new Properties(); + } else { + this.connectionProperties = (Properties) connectionProperties.clone(); + } + propertySupport.firePropertyChange(PROP_CONNECTIONPROPERTIES, old, connectionProperties); + } + /** Returns user schema name */ @Override public String getSchema() { @@ -732,9 +766,16 @@ throw new DDLException(NbBundle.getMessage(DatabaseConnection.class, "EXC_InsufficientConnInfo")); // NOI18N } - Properties dbprops = new Properties(); + Properties dbprops; + if (connectionProperties != null) { + dbprops = getConnectionProperties(); + } else { + dbprops = new Properties(); + } if ((usr != null) && (usr.length() > 0)) { dbprops.put("user", usr); //NOI18N + } + if ((pwd != null) && (pwd.length() > 0)) { dbprops.put("password", pwd); //NOI18N } @@ -812,11 +853,16 @@ sendException(new DDLException(NbBundle.getMessage(DatabaseConnection.class, "EXC_InsufficientConnInfo"))); } - Properties dbprops = new Properties(); - if ( usr.length() > 0 ) { + Properties dbprops; + if (connectionProperties != null) { + dbprops = getConnectionProperties(); + } else { + dbprops = new Properties(); + } + if ((usr != null) && (usr.length() > 0)) { dbprops.put("user", usr); //NOI18N } - if ((pwd != null && pwd.length() > 0)) { + if ((pwd != null) && (pwd.length() > 0)) { dbprops.put("password", pwd); //NOI18N } @@ -1005,9 +1051,17 @@ */ @Override public boolean equals(Object obj) { - if (obj instanceof DBConnection) { - DBConnection conn = (DBConnection) obj; - return toString().equals(conn.toString()); + if (obj instanceof DatabaseConnection) { + DatabaseConnection conn = (DatabaseConnection) obj; + if (toString().equals(conn.toString())) { + if ((connectionProperties == null + && conn.getConnectionProperties() == null)) { + return true; + } else if (connectionProperties != null) { + return connectionProperties.equals( + conn.getConnectionProperties()); + } + } } return false; @@ -1029,6 +1083,11 @@ //IGNORE - drvname not stored in 3.6 and earlier //IGNORE - displayName not stored in 6.7 and earlier } + try { + connectionProperties = (Properties) in.readObject(); + } catch (Exception ex) { + //IGNORE - connectionProperties not stored in 7.3 and earlier + } // boston setting/pilsen setting? if ((name != null) && (name.equals(DatabaseConnection.SUPPORT))) { @@ -1052,6 +1111,7 @@ out.writeObject(DatabaseConnection.SUPPORT); out.writeObject(drvname); out.writeObject(displayName); + out.writeObject(connectionProperties); } @Override diff --git a/db/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertor.java b/db/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertor.java --- a/db/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertor.java +++ b/db/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertor.java @@ -60,6 +60,7 @@ import java.nio.charset.CoderResult; import java.util.LinkedList; import java.util.Map; +import java.util.Properties; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -237,7 +238,8 @@ handler.driverName, handler.connectionUrl, handler.schema, - handler.user); + handler.user, + handler.connectionProperties); dbconn.setConnectionFileName(handler.connectionFileName); if (handler.displayName != null) { dbconn.setDisplayName(handler.displayName); @@ -402,6 +404,19 @@ } else { DatabaseConnection.deletePassword(name); } + if (instance.getConnectionProperties() != null) { + Properties p = instance.getConnectionProperties(); + for (String key : p.stringPropertyNames()) { + pw.println(" "); //NOI18N + pw.print(" "); //NOI18N + pw.print(XMLUtil.toElementContent(key)); + pw.println(""); //NOI18N + pw.print(" "); //NOI18N + pw.print(XMLUtil.toElementContent(p.getProperty(key))); + pw.println(""); //NOI18N + pw.println(" "); //NOI18N + } + } pw.println(""); //NOI18N } } @@ -418,19 +433,29 @@ private static final String ELEMENT_USER = "user"; // NOI18N private static final String ELEMENT_PASSWORD = "password"; // NOI18N private static final String ELEMENT_DISPLAY_NAME = "display-name"; // NOI18N + private static final String ELEMENT_CONNECTION_PROPERTY = "connection-property"; // NOI18N + private static final String ELEMENT_CONNECTION_PROPERTY_NAME = "name"; // NOI18N + private static final String ELEMENT_CONNECTION_PROPERTY_VALUE = "value"; // NOI18N private static final String ATTR_PROPERTY_VALUE = "value"; // NOI18N final String connectionFileName; + private boolean readingProperty = false; + private String propertyName; + private String propertyValue; + private StringBuilder buffer = new StringBuilder(); + String driverClass; String driverName; String connectionUrl; String schema; String user; String displayName; + Properties connectionProperties; public Handler(String connectionFileName) { this.connectionFileName = connectionFileName; + this.connectionProperties = new Properties(); } @Override @@ -457,6 +482,14 @@ user = value; } else if (ELEMENT_DISPLAY_NAME.equals(qName)) { displayName = value; + } else if (ELEMENT_CONNECTION_PROPERTY.equals(qName)) { + readingProperty = true; + propertyName = ""; //NOI18N + propertyValue = ""; //NOI18N + } else if (readingProperty && ELEMENT_CONNECTION_PROPERTY_NAME.equals(qName)) { + buffer.setLength(0); + } else if (readingProperty && ELEMENT_CONNECTION_PROPERTY_VALUE.equals(qName)) { + buffer.setLength(0); } else if (ELEMENT_PASSWORD.equals(qName)) { // reading old settings byte[] bytes = null; @@ -482,6 +515,35 @@ } } } + + @Override + public void ignorableWhitespace(char[] chars, int start, int length) throws SAXException { + if (readingProperty) { + buffer.append(chars, start, length); + } + } + + @Override + public void characters(char[] chars, int start, int length) throws SAXException { + if (readingProperty) { + buffer.append(chars, start, length); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (readingProperty && ELEMENT_CONNECTION_PROPERTY.equals(qName)) { + connectionProperties.put(propertyName, propertyValue); + readingProperty = false; + propertyName = ""; + propertyValue = ""; + buffer.setLength(0); + } else if (readingProperty && ELEMENT_CONNECTION_PROPERTY_NAME.equals(qName)) { + propertyName = buffer.toString(); + } else if (readingProperty && ELEMENT_CONNECTION_PROPERTY_VALUE.equals(qName)) { + propertyValue = buffer.toString(); + } + } } private final class PCL implements PropertyChangeListener, Runnable { diff --git a/db/src/org/netbeans/modules/db/explorer/dlg/Bundle.properties b/db/src/org/netbeans/modules/db/explorer/dlg/Bundle.properties --- a/db/src/org/netbeans/modules/db/explorer/dlg/Bundle.properties +++ b/db/src/org/netbeans/modules/db/explorer/dlg/Bundle.properties @@ -386,3 +386,5 @@ ChooseConnectionNamePanel.Name=Choose name for connection ConnectionNameDialogText=&Input connection name: MSG_ConnectionNamePanelComment=Override the default name for the connection. The name should be descriptive about the connection you are creating. +NewConnectionPanel.bConnectionProperties=Connection &Properties +NewConnectionPanel.dlgConnectionProperties=Connection Properties diff --git a/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.form b/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.form --- a/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.form +++ b/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.form @@ -27,7 +27,8 @@ - + + @@ -47,29 +48,36 @@ - - - - - - - + + + + + + + + + + + + + - + - - - - - - + + + + + + + - + @@ -142,8 +150,11 @@ - - + + + + + @@ -415,5 +426,15 @@ + + + + + + + + + + diff --git a/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.java b/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.java --- a/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.java +++ b/db/src/org/netbeans/modules/db/explorer/dlg/NewConnectionPanel.java @@ -53,6 +53,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map.Entry; +import java.util.Properties; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -75,6 +76,10 @@ import org.netbeans.modules.db.explorer.ConnectionList; import org.netbeans.modules.db.util.DatabaseExplorerInternalUIs; import org.netbeans.modules.db.util.JdbcUrl; +import org.netbeans.modules.db.util.PropertyEditorPanel; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; import org.openide.WizardValidationException; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; @@ -93,6 +98,7 @@ private Set knownConnectionNames = new HashSet(); private static final Logger LOGGER = Logger.getLogger(NewConnectionPanel.class.getName()); private final ConnectionPanel wp; + private Properties connectionProperties = new Properties(); private void initFieldMap() { // These should be in the order of display on the form, so that we correctly @@ -211,6 +217,7 @@ setUrlField(); updateFieldsFromUrl(); setUpFields(); + connectionProperties = connection.getConnectionProperties(); DocumentListener docListener = new DocumentListener() { @@ -312,6 +319,7 @@ passwordCheckBox = new javax.swing.JCheckBox(); directUrlLabel = new javax.swing.JLabel(); bTestConnection = new javax.swing.JButton(); + bConnectionProperties = new javax.swing.JButton(); FormListener formListener = new FormListener(); @@ -392,6 +400,9 @@ org.openide.awt.Mnemonics.setLocalizedText(bTestConnection, org.openide.util.NbBundle.getMessage(NewConnectionPanel.class, "NewConnectionPanel.bTestConnection")); // NOI18N bTestConnection.addActionListener(formListener); + org.openide.awt.Mnemonics.setLocalizedText(bConnectionProperties, org.openide.util.NbBundle.getMessage(NewConnectionPanel.class, "NewConnectionPanel.bConnectionProperties")); // NOI18N + bConnectionProperties.addActionListener(formListener); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -399,7 +410,8 @@ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() + .addComponent(directUrlLabel, javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(hostLabel) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) @@ -416,26 +428,31 @@ .addComponent(userLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addGap(8, 8, 8) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(userField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(sidField, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(serviceField, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(tnsField, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(dsnField, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(serverNameField, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(instanceField, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() - .addComponent(hostField, javax.swing.GroupLayout.DEFAULT_SIZE, 230, Short.MAX_VALUE) + .addComponent(bConnectionProperties) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(bTestConnection) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 100, Short.MAX_VALUE)) + .addComponent(userField, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(sidField) + .addComponent(serviceField) + .addComponent(tnsField) + .addComponent(dsnField) + .addComponent(serverNameField) + .addComponent(instanceField) + .addGroup(layout.createSequentialGroup() + .addComponent(hostField) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(portLabel) .addGap(2, 2, 2) .addComponent(portField, javax.swing.GroupLayout.PREFERRED_SIZE, 105, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(databaseField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(passwordField, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(bTestConnection) + .addComponent(databaseField, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(passwordField) + .addComponent(urlField, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(templateComboBox, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() .addComponent(passwordCheckBox, javax.swing.GroupLayout.PREFERRED_SIZE, 256, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(urlField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) - .addComponent(templateComboBox, javax.swing.GroupLayout.Alignment.TRAILING, 0, 383, Short.MAX_VALUE))) - .addComponent(directUrlLabel, javax.swing.GroupLayout.Alignment.LEADING)) + .addGap(0, 0, Short.MAX_VALUE))))) .addContainerGap()) ); layout.setVerticalGroup( @@ -492,7 +509,9 @@ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(passwordCheckBox) .addGap(26, 26, 26) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(bTestConnection) + .addComponent(bConnectionProperties)) .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(urlField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -519,6 +538,9 @@ else if (evt.getSource() == bTestConnection) { NewConnectionPanel.this.bTestConnectionActionPerformed(evt); } + else if (evt.getSource() == bConnectionProperties) { + NewConnectionPanel.this.bConnectionPropertiesActionPerformed(evt); + } } public void focusGained(java.awt.event.FocusEvent evt) { @@ -590,7 +612,22 @@ private void bTestConnectionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bTestConnectionActionPerformed tryConnection(); }//GEN-LAST:event_bTestConnectionActionPerformed + + private void bConnectionPropertiesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bConnectionPropertiesActionPerformed + PropertyEditorPanel pep = new PropertyEditorPanel(connectionProperties, true); + DialogDescriptor dd = new DialogDescriptor( + pep, + NbBundle.getMessage(NewConnectionPanel.class, "NewConnectionPanel.dlgConnectionProperties"), + true, + null); + Object result = DialogDisplayer.getDefault().notify(dd); + if(result == NotifyDescriptor.OK_OPTION) { + connectionProperties = pep.getValue(); + } + }//GEN-LAST:event_bConnectionPropertiesActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bConnectionProperties; private javax.swing.JButton bTestConnection; private javax.swing.JTextField databaseField; private javax.swing.JLabel databaseLabel; @@ -632,10 +669,10 @@ } connection.setDatabase(urlField.getText()); - connection.setUser(userField.getText()); connection.setPassword(getPassword()); connection.setRememberPassword(passwordCheckBox.isSelected()); + connection.setConnectionProperties(connectionProperties); } private void resize() { @@ -816,6 +853,7 @@ passwordCheckBox.setEnabled(enable); urlField.setEnabled(enable); bTestConnection.setEnabled(enable); + bConnectionProperties.setEnabled(enable); for (Entry entry : urlFields.entrySet()) { entry.getValue().getField().setEnabled(enable); diff --git a/db/src/org/netbeans/modules/db/explorer/node/ConnectionNode.java b/db/src/org/netbeans/modules/db/explorer/node/ConnectionNode.java --- a/db/src/org/netbeans/modules/db/explorer/node/ConnectionNode.java +++ b/db/src/org/netbeans/modules/db/explorer/node/ConnectionNode.java @@ -48,6 +48,7 @@ import java.io.IOException; import java.sql.Connection; import java.sql.DatabaseMetaData; +import java.util.Properties; import javax.swing.Action; import org.netbeans.api.db.explorer.DatabaseException; import org.netbeans.api.db.explorer.DatabaseMetaDataTransfer; @@ -64,8 +65,10 @@ import org.netbeans.modules.db.explorer.metadata.MetadataModelManager; import org.netbeans.modules.db.metadata.model.api.MetadataModel; import org.netbeans.modules.db.metadata.model.api.MetadataModels; +import org.netbeans.modules.db.util.PropertiesEditor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; +import org.openide.nodes.Sheet; import org.openide.util.Exceptions; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; @@ -81,6 +84,8 @@ private static final String CONNECTEDICONBASE = "org/netbeans/modules/db/resources/connection.gif"; // NOI18N private static final String DISCONNECTEDICONBASE = "org/netbeans/modules/db/resources/connectionDisconnected.gif"; // NOI18N + private static final String CONNECTIONPROPERTIES = "ConnectionProperties"; //NOI18N + private static final String CONNECTIONPROPERTIESDESC = "ConnectionPropertiesDescription"; //NOI18N private static final String FOLDER = "Connection"; // NOI18N private static final RequestProcessor RP = new RequestProcessor(ConnectionNode.class.getName()); @@ -160,6 +165,8 @@ } else if (nps.getName().equals(DISPLAYNAME)) { setDisplayName(val.toString()); refreshNode = false; + } else if (nps.getName().equals(CONNECTIONPROPERTIES)) { + connection.setConnectionProperties((Properties) val); } super.setPropertyValue(nps, val); @@ -181,6 +188,10 @@ addProperty(USER, USERDESC, String.class, !connected, connection.getUser()); addProperty(REMEMBERPW, REMEMBERPWDESC, Boolean.class, !connected, connection.rememberPassword()); + addProperty(CONNECTIONPROPERTIES, CONNECTIONPROPERTIESDESC, Properties.class, !connected, connection.getConnectionProperties()); + Property ps = getSheet().get(Sheet.PROPERTIES).get(CONNECTIONPROPERTIES); + ps.setValue("canEditAsText", Boolean.FALSE); //NOI18N + ps.setValue(NodePropertySupport.CUSTOM_EDITOR, PropertiesEditor.class); if (connected) { Specification spec = connection.getConnector().getDatabaseSpecification(); diff --git a/db/src/org/netbeans/modules/db/explorer/node/NodePropertySupport.java b/db/src/org/netbeans/modules/db/explorer/node/NodePropertySupport.java --- a/db/src/org/netbeans/modules/db/explorer/node/NodePropertySupport.java +++ b/db/src/org/netbeans/modules/db/explorer/node/NodePropertySupport.java @@ -42,6 +42,7 @@ package org.netbeans.modules.db.explorer.node; +import java.beans.PropertyEditor; import java.lang.reflect.InvocationTargetException; import org.netbeans.api.db.explorer.node.BaseNode; import org.openide.nodes.PropertySupport; @@ -51,6 +52,8 @@ * @author Rob Englander */ public class NodePropertySupport extends PropertySupport { + public static final String CUSTOM_EDITOR = "NodePropertySupport.customEditor"; //NOI18N + public static final String NODE = "NodePropertySupport.Node"; //NOI18N private BaseNode node; private String key; @@ -59,6 +62,7 @@ super(name, type, displayName, shortDescription, true, writable); key = name; this.node = node; + setValue(NODE, node); } @Override @@ -76,4 +80,37 @@ node.setPropertyValue(this, val); } + /** + * PropertyEditor can be set via setValue - it can be either instanciated or + * a Class, that has a Default-Constructor and results in an object, that + * implements PropertyEditor + * + * @return + */ + @Override + public PropertyEditor getPropertyEditor() { + PropertyEditor result = null; + Object potentialEditor = getValue(CUSTOM_EDITOR); + + if (potentialEditor instanceof PropertyEditor) { + result = (PropertyEditor) potentialEditor; + } else if (potentialEditor instanceof Class) { + try { + potentialEditor = ((Class) potentialEditor).newInstance(); + if (!(potentialEditor instanceof PropertyEditor)) { + throw new IllegalArgumentException( + "Editor class does not derive from property editor"); //NOI18N } + return (PropertyEditor) potentialEditor; + } catch (InstantiationException ex) { + throw new RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + if (result == null) { + result = super.getPropertyEditor(); + } + return result; + } +} diff --git a/db/src/org/netbeans/modules/db/util/Bundle.properties b/db/src/org/netbeans/modules/db/util/Bundle.properties --- a/db/src/org/netbeans/modules/db/util/Bundle.properties +++ b/db/src/org/netbeans/modules/db/util/Bundle.properties @@ -71,3 +71,9 @@ =TNS Name =Additional Properties ErrorInfoPanel.iconLabel.text= + +NoPropertiesSet=No properties set +PropertyEditorPanel.propertyTable.columnModel.title1=Value +PropertyEditorPanel.propertyTable.columnModel.title0=Property +PropertyEditorPanel.addRowButton.text=Add Property +PropertyEditorPanel.removeRowButton.text=Remove Property diff --git a/db/src/org/netbeans/modules/db/util/PropertiesEditor.java b/db/src/org/netbeans/modules/db/util/PropertiesEditor.java new file mode 100644 --- /dev/null +++ b/db/src/org/netbeans/modules/db/util/PropertiesEditor.java @@ -0,0 +1,116 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.db.util; + +import java.beans.FeatureDescriptor; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyEditorSupport; +import java.util.Properties; +import org.openide.explorer.propertysheet.ExPropertyEditor; +import org.openide.explorer.propertysheet.PropertyEnv; +import org.openide.nodes.Node; +import org.openide.util.NbBundle; + +/** + * Custom editor for properties - mainly exists to call custom editor + * + * @author Matthias Bläsing + */ +public class PropertiesEditor extends PropertyEditorSupport implements ExPropertyEditor { + + private boolean canWrite = true; + + @Override + public String getAsText() { + Properties value = (Properties) getValue(); + if (value == null || value.size() == 0) { + return NbBundle.getMessage(PropertiesEditor.class, + "NoPropertiesSet"); //NOI18N + } else { + return value.toString(); + } + } + + /** + * Can't be called and throws IllegalArgumentException + */ + @Override + public void setAsText(String text) throws IllegalArgumentException { + throw new IllegalArgumentException("Can't be set by setAsText");//NOI18N + } + + @Override + public String getJavaInitializationString() { + return null; // does not generate any code + } + + @Override + public boolean supportsCustomEditor() { + return true; + } + + @Override + public java.awt.Component getCustomEditor() { + PropertyEditorPanel pep = new PropertyEditorPanel( + (Properties) this.getValue(), canWrite); + pep.addPropertyChangeListener(PropertyEditorPanel.PROP_VALUE, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + setValue(((PropertyEditorPanel) pce.getSource()).getValue()); + } + }); + return pep; + } + + @Override + public void attachEnv(PropertyEnv env) { + FeatureDescriptor d = env.getFeatureDescriptor(); + if (d instanceof Node.Property) { + canWrite = ((Node.Property) d).canWrite(); + } + } + + public boolean isEditable() { + return canWrite; + } +} \ No newline at end of file diff --git a/db/src/org/netbeans/modules/db/util/PropertyEditorPanel.form b/db/src/org/netbeans/modules/db/util/PropertyEditorPanel.form new file mode 100644 --- /dev/null +++ b/db/src/org/netbeans/modules/db/util/PropertyEditorPanel.form @@ -0,0 +1,97 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <ResourceString bundle="org/netbeans/modules/db/util/Bundle.properties" key="PropertyEditorPanel.propertyTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + + + + + + + <ResourceString bundle="org/netbeans/modules/db/util/Bundle.properties" key="PropertyEditorPanel.propertyTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + + + + + + + + + + + + + + + + diff --git a/db/src/org/netbeans/modules/db/util/PropertyEditorPanel.java b/db/src/org/netbeans/modules/db/util/PropertyEditorPanel.java new file mode 100644 --- /dev/null +++ b/db/src/org/netbeans/modules/db/util/PropertyEditorPanel.java @@ -0,0 +1,251 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ + +/* + * PropertyEditorPanel.java + * + * Created on 01.04.2011, 20:25:24 + */ +package org.netbeans.modules.db.util; + +import java.util.Arrays; +import java.util.Properties; +import java.util.Vector; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableModel; + +/** + * Custom implentation for a property editor, as the build in doesn't work to + * well with international characters + * + * @author Matthias Bläsing + */ +public class PropertyEditorPanel extends javax.swing.JPanel { + + public static final String PROP_VALUE = "value"; + private Properties value; + private boolean editable; + private boolean updateing; + + public PropertyEditorPanel(Properties initalValue, boolean editable) { + initComponents(); + this.value = initalValue; + this.editable = editable; + propertyTable.putClientProperty( + "terminateEditOnFocusLost", Boolean.TRUE); //NOI18N + updateTableFromEditor(); + final TableModel tm = propertyTable.getModel(); + tm.addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent tme) { + synchronized (PropertyEditorPanel.this) { + if (updateing) { + return; + } + updateing = true; + Properties p = new Properties(); + for (int i = 0; i < tm.getRowCount(); i++) { + p.setProperty((String) tm.getValueAt(i, 0), (String) tm.getValueAt(i, 1)); + } + Properties oldValue = value; + value = p; + firePropertyChange(PROP_VALUE, oldValue, value); + updateing = false; + } + } + }); + propertyTable.getSelectionModel().addListSelectionListener( + new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent lse) { + updateRemoveButtonSensible(); + } + }); + updateAddButtonSensible(); + updateRemoveButtonSensible(); + } + + private void updateAddButtonSensible() { + if (this.editable) { + addRowButton.setEnabled(true); + } else { + addRowButton.setEnabled(false); + } + } + + private void updateRemoveButtonSensible() { + if (this.editable && propertyTable.getSelectedRowCount() > 0) { + removeRowButton.setEnabled(true); + } else { + removeRowButton.setEnabled(false); + } + } + + @SuppressWarnings("unchecked") + private void updateTableFromEditor() { + synchronized (this) { + if (updateing) { + return; + } + updateing = true; + DefaultTableModel dtm = (DefaultTableModel) propertyTable.getModel(); + Vector columns = new Vector(2); + Vector values = new Vector(); + columns.add(dtm.getColumnName(0)); + columns.add(dtm.getColumnName(1)); + if (value != null) { + for (String key : value.stringPropertyNames()) { + Vector row = new Vector(2); + row.add(key); + row.add(value.getProperty(key, "")); + values.add(row); + } + } + dtm.setDataVector(values, columns); + updateing = false; + } + } + + public Properties getValue() { + return value; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonPanel = new javax.swing.JPanel(); + addRowButton = new javax.swing.JButton(); + removeRowButton = new javax.swing.JButton(); + propertyScrollPane = new javax.swing.JScrollPane(); + propertyTable = new javax.swing.JTable(); + + setLayout(new java.awt.BorderLayout()); + + buttonPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT)); + + addRowButton.setText(org.openide.util.NbBundle.getMessage(PropertyEditorPanel.class, "PropertyEditorPanel.addRowButton.text")); // NOI18N + addRowButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addRowButtonActionPerformed(evt); + } + }); + buttonPanel.add(addRowButton); + + removeRowButton.setText(org.openide.util.NbBundle.getMessage(PropertyEditorPanel.class, "PropertyEditorPanel.removeRowButton.text")); // NOI18N + removeRowButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + removeRowButtonActionPerformed(evt); + } + }); + buttonPanel.add(removeRowButton); + + add(buttonPanel, java.awt.BorderLayout.PAGE_END); + + propertyTable.setAutoCreateRowSorter(true); + propertyTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "Property", "Value" + } + ) { + Class[] types = new Class [] { + java.lang.String.class, java.lang.String.class + }; + + public Class getColumnClass(int columnIndex) { + return types [columnIndex]; + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return PropertyEditorPanel.this.editable; + } + }); + propertyTable.setColumnSelectionAllowed(true); + propertyScrollPane.setViewportView(propertyTable); + propertyTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + propertyTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PropertyEditorPanel.class, "PropertyEditorPanel.propertyTable.columnModel.title0")); // NOI18N + propertyTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PropertyEditorPanel.class, "PropertyEditorPanel.propertyTable.columnModel.title1")); // NOI18N + + add(propertyScrollPane, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + private void addRowButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addRowButtonActionPerformed + DefaultTableModel dtm = (DefaultTableModel) propertyTable.getModel(); + dtm.addRow(new Object[]{"", ""}); + }//GEN-LAST:event_addRowButtonActionPerformed + + private void removeRowButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeRowButtonActionPerformed + int[] viewRows = propertyTable.getSelectedRows(); + int[] modelRows = new int[viewRows.length]; + + for (int i = 0; i < viewRows.length; i++) { + modelRows[i] = propertyTable.convertRowIndexToModel(viewRows[i]); + } + + Arrays.sort(modelRows); + + DefaultTableModel dtm = (DefaultTableModel) propertyTable.getModel(); + + for (int i = modelRows.length - 1; i >= 0; i--) { + dtm.removeRow(modelRows[i]); + } + }//GEN-LAST:event_removeRowButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton addRowButton; + private javax.swing.JPanel buttonPanel; + private javax.swing.JScrollPane propertyScrollPane; + private javax.swing.JTable propertyTable; + private javax.swing.JButton removeRowButton; + // End of variables declaration//GEN-END:variables +} diff --git a/db/test/unit/src/org/netbeans/api/db/explorer/DatabaseConnectionTest.java b/db/test/unit/src/org/netbeans/api/db/explorer/DatabaseConnectionTest.java --- a/db/test/unit/src/org/netbeans/api/db/explorer/DatabaseConnectionTest.java +++ b/db/test/unit/src/org/netbeans/api/db/explorer/DatabaseConnectionTest.java @@ -45,6 +45,7 @@ package org.netbeans.api.db.explorer; import java.sql.Connection; +import java.util.Properties; import org.netbeans.modules.db.test.Util; import org.netbeans.modules.db.test.DBTestBase; @@ -231,6 +232,32 @@ Util.clearConnections(); } + /** + * Test that additional connection properties are set and get correctly. + */ + public void testGetConnectionProperties() throws Exception { + DatabaseConnection nullPropertiesConn = DatabaseConnection.create( + getJDBCDriver(), getDbUrl(), getUsername(), getSchema(), + getPassword(), false, "Test", null); + Properties p = nullPropertiesConn.getConnectionProperties(); + assertNotNull(p); + assertTrue("Properties object should be empty", p.keySet().isEmpty()); + + Properties testConnProps = new Properties(); + testConnProps.put("testKey", "testValue"); + DatabaseConnection somePopertiesConn = DatabaseConnection.create( + getJDBCDriver(), getDbUrl(), getUsername(), getSchema(), + getPassword(), false, "Test", testConnProps); + Properties returnedProps = somePopertiesConn.getConnectionProperties(); + assertEquals(1, returnedProps.keySet().size()); + assertEquals("testValue", returnedProps.get("testKey")); + + returnedProps.put("addedKey", "addedValue"); + Properties returnedAgain = somePopertiesConn.getConnectionProperties(); + assertEquals("Internal properties should not be affected by changes", + 1, returnedAgain.keySet().size()); + } + private static boolean connectionIsValid(Connection conn) throws Exception { return org.netbeans.modules.db.explorer.DatabaseConnection.isVitalConnection(conn, null); } diff --git a/db/test/unit/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertorTest.java b/db/test/unit/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertorTest.java --- a/db/test/unit/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertorTest.java +++ b/db/test/unit/src/org/netbeans/modules/db/explorer/DatabaseConnectionConvertorTest.java @@ -147,7 +147,7 @@ } public void testSaveOnPropertyChange() throws Exception { - DatabaseConnection dbconn = new DatabaseConnection("a", "b", "c", "d", "e", null); + DatabaseConnection dbconn = new DatabaseConnection("a", "b", "c", "d", "e", (String) null); FileObject fo = DatabaseConnectionConvertor.create(dbconn).getPrimaryFile(); class FCL extends FileChangeAdapter {