001: /*
002: * uDig - User Friendly Desktop Internet GIS client
003: * http://udig.refractions.net
004: * (C) 2005, Refractions Research Inc.
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package net.refractions.udig.catalog.internal.db2.ui;
018:
019: import java.io.Serializable;
020: import java.lang.reflect.InvocationTargetException;
021: import java.net.URL;
022: import java.sql.Connection;
023: import java.sql.DriverManager;
024: import java.sql.ResultSet;
025: import java.sql.SQLException;
026: import java.sql.Statement;
027: import java.util.ArrayList;
028: import java.util.Arrays;
029: import java.util.HashMap;
030: import java.util.List;
031: import java.util.Map;
032:
033: import net.refractions.udig.catalog.IService;
034: import net.refractions.udig.catalog.db2.DB2Plugin;
035: import net.refractions.udig.catalog.db2.internal.Messages;
036: import net.refractions.udig.catalog.internal.db2.DB2ServiceExtension;
037: import net.refractions.udig.catalog.ui.CatalogUIPlugin;
038: import net.refractions.udig.catalog.ui.UDIGConnectionPage;
039: import net.refractions.udig.catalog.ui.preferences.AbstractProprietaryDatastoreWizardPage;
040: import net.refractions.udig.catalog.ui.preferences.AbstractProprietaryJarPreferencePage;
041: import net.refractions.udig.ui.PlatformGIS;
042:
043: import org.eclipse.core.runtime.IProgressMonitor;
044: import org.eclipse.jface.dialogs.IDialogSettings;
045: import org.eclipse.jface.operation.IRunnableWithProgress;
046: import org.eclipse.swt.SWT;
047: import org.eclipse.swt.custom.CCombo;
048: import org.eclipse.swt.events.ModifyEvent;
049: import org.eclipse.swt.events.ModifyListener;
050: import org.eclipse.swt.widgets.Composite;
051: import org.eclipse.swt.widgets.Event;
052: import org.eclipse.swt.widgets.Group;
053: import org.eclipse.swt.widgets.Text;
054: import org.geotools.data.DataStoreFactorySpi;
055: import org.geotools.data.DataStoreFactorySpi.Param;
056: import org.geotools.data.db2.DB2ConnectionFactory;
057: import org.geotools.data.db2.DB2DataStoreFactory;
058:
059: /**
060: * Specify DB2 database connection parameters.
061: * <p>
062: * </p>
063: *
064: * @author Justin Deoliveira,Refractions Research Inc.,jdeolive@refractions.net
065: * @since 1.0.1
066: */
067: public class DB2WizardPage extends
068: AbstractProprietaryDatastoreWizardPage implements
069: UDIGConnectionPage {
070:
071: private static final int COMBO_HISTORY_LENGTH = 15;
072: private static final String DB2_RECENT = "DB2_RECENT"; //$NON-NLS-1$
073: private static final String DB2_WIZARD = "DB2_WIZARD"; //$NON-NLS-1$
074: private static DB2DataStoreFactory factory = new DB2DataStoreFactory();
075: ArrayList<DataBaseConnInfo> dbData;
076: boolean dirty = true;
077:
078: /** DB2WizardPage IMAGE_KEY field - not sure what it is used for */
079: public final String IMAGE_KEY = "DB2PageImage"; //$NON-NLS-1$
080: Connection realConnection;
081: private IDialogSettings settings;
082: private DB2Preferences preferences;
083:
084: /**
085: * Constructs a DB2 database connection wizard page. Reads any settings that may have been saved
086: * from a previous session.
087: */
088: public DB2WizardPage() {
089: super (Messages.DB2WizardPage_title);
090: this .settings = DB2Plugin.getDefault().getDialogSettings()
091: .getSection(DB2_WIZARD);
092: if (this .settings == null) {
093: this .settings = DB2Plugin.getDefault().getDialogSettings()
094: .addNewSection(DB2_WIZARD);
095: }
096: }
097:
098: /**
099: * Adds an entry to a history, while taking care of duplicate history items and excessively long
100: * histories. The assumption is made that all histories should be of length
101: * <code>COMBO_HISTORY_LENGTH</code>.
102: *
103: * @param history the current history
104: * @param newEntry the entry to add to the history Stolen from
105: * org.eclipse.team.internal.ccvs.ui.wizards.ConfigurationWizardMainPage
106: */
107: private void addToHistory(List<String> history, String newEntry) {
108: history.remove(newEntry);
109: history.add(0, newEntry);
110:
111: // since only one new item was added, we can be over the limit
112: // by at most one item
113: if (history.size() > COMBO_HISTORY_LENGTH)
114: history.remove(COMBO_HISTORY_LENGTH);
115: }
116:
117: /**
118: * Adds an entry to a history, while taking care of duplicate history items and excessively long
119: * histories. The assumption is made that all histories should be of length
120: * <code>COMBO_HISTORY_LENGTH</code>.
121: *
122: * @param history the current history
123: * @param newEntry the entry to add to the history
124: * @return the history with the new entry appended Stolen from
125: * org.eclipse.team.internal.ccvs.ui.wizards.ConfigurationWizardMainPage
126: */
127: private String[] addToHistory(String[] history, String newEntry) {
128: ArrayList<String> l = new ArrayList<String>(Arrays
129: .asList(history));
130: addToHistory(l, newEntry);
131: String[] r = new String[l.size()];
132: l.toArray(r);
133: return r;
134: }
135:
136: /**
137: * Checks if all user input fields are non-empty.
138: *
139: * @return true if all needed fields are non-empty.
140: */
141:
142: protected boolean areAllFieldsFilled() {
143: if (getSchema().length() == 0)
144: return false;
145: return areDbFieldsFilled();
146: }
147:
148: /**
149: * Checks if port, host, userid, password and database name fields all are non-empty.
150: *
151: * @return true if all needed fields are non-empty.
152: */
153: protected boolean areDbFieldsFilled() {
154: if (!DB2Preferences.isInstalled())
155: return false;
156:
157: if ((getPortText().length() == 0)
158: || (getHostText().length() == 0)
159: || (getUserText().length() == 0)
160: || (getPassText().length() == 0)
161: || (getDBText().length() == 0)) {
162: return false;
163: }
164: return true;
165: }
166:
167: /**
168: * Perform additional processing when the GUI is created. Sets the default value for the port
169: * field and disables the schema field.
170: *
171: * @param arg0
172: * @return null
173: */
174: @Override
175: protected Group createAdvancedControl(Composite arg0) {
176: return null;
177: }
178:
179: private String emptyAsNull(String value) {
180: if (value.length() == 0)
181: return null;
182: return value;
183: }
184:
185: /**
186: * Always returns false as we want to keep all schema candidates.
187: *
188: * @param schemaName
189: * @return false
190: */
191: @Override
192: protected boolean excludeSchema(String schemaName) {
193: return false;
194: }
195:
196: /**
197: * Gets a connection to the DB2 database. The port, host, userid, password and database name
198: * must have been specified in order for the connection to succeed.
199: *
200: * @return a database Connection
201: * @throws Exception
202: */
203: @Override
204: protected Connection getConnection() {
205:
206: final String portText = getPortText();
207: final String hostText = getHostText();
208: final String userText = getUserText();
209: final String passText = getPassText();
210: final String db = getDBText();
211:
212: if (!areDbFieldsFilled()) {
213: return null;
214: }
215:
216: if (this .dirty || this .realConnection == null) {
217: this .dirty = false;
218:
219: try {
220: getContainer().run(true, true,
221: new IRunnableWithProgress() {
222:
223: public void run(IProgressMonitor monitor)
224: throws InvocationTargetException,
225: InterruptedException {
226: PlatformGIS.runBlockingOperation(
227: new IRunnableWithProgress() {
228:
229: public void run(
230: IProgressMonitor monitor)
231: throws InvocationTargetException,
232: InterruptedException {
233: monitor
234: .beginTask(
235: Messages.DB2WizardPage_connectionTask,
236: IProgressMonitor.UNKNOWN);
237: if (DB2WizardPage.this .realConnection != null)
238: try {
239: DB2WizardPage.this .realConnection
240: .close();
241: } catch (SQLException e1) {
242: // it's dead anyhow
243: }
244:
245: DB2ConnectionFactory connFac = new DB2ConnectionFactory(
246: hostText,
247: portText, db);
248: connFac.setLogin(
249: userText,
250: passText);
251: DriverManager
252: .setLoginTimeout(3);
253: try {
254: DB2WizardPage.this .realConnection = connFac
255: .getConnectionPool()
256: .getConnection();
257: if (DB2WizardPage.this .realConnection != null) {
258: DB2WizardPage.this .database
259: .getDisplay()
260: .asyncExec(
261: new Runnable() {
262: public void run() {
263: DB2WizardPage.this .database
264: .notifyListeners(
265: SWT.FocusIn,
266: new Event());
267:
268: }
269: });
270: }
271: } catch (SQLException e) {
272: throw new InvocationTargetException(
273: e,
274: e
275: .getLocalizedMessage());
276: }
277: }
278:
279: }, monitor);
280: }
281: });
282: } catch (InvocationTargetException e2) {
283: preferences.performDefaults();
284: throw new RuntimeException(e2.getLocalizedMessage(), e2);
285: } catch (InterruptedException e2) {
286: // Don't know why this exception doesn't do anything.
287: }
288: }
289:
290: return this .realConnection;
291: }
292:
293: /**
294: * Returns the DB2DataStoreFactory.
295: *
296: * @return the DB2DataStoreFactory
297: */
298: @Override
299: protected DataStoreFactorySpi getDataStoreFactorySpi() {
300: return factory;
301: }
302:
303: protected String getDBText() {
304: return ((Text) this .database).getText();
305: }
306:
307: protected String getHostText() {
308: return ((CCombo) this .host).getText();
309: }
310:
311: /**
312: * Returns a string with the name of the DB2 plugin
313: *
314: * @return the DB2 plugin name
315: */
316: public String getId() {
317: return "net.refractions.udig.catalog.ui.db2"; //$NON-NLS-1$
318: }
319:
320: /**
321: * Returns the parameters Empty strings are converted to null to work correctly with
322: * factory.canProcess.
323: *
324: * @return a map of parameter values
325: */
326: @Override
327: public Map<String, Serializable> getParams() {
328:
329: if (!areDbFieldsFilled() || getConnection() == null) {
330: return null;
331: }
332:
333: Map<String, Serializable> params = new HashMap<String, Serializable>();
334: Param[] dbParams = factory.getParametersInfo();
335: params.put(dbParams[0].key, "db2"); //$NON-NLS-1$
336: params.put(dbParams[1].key, emptyAsNull(getHostText()));
337: String dbport = emptyAsNull(getPortText());
338: try {
339: params.put(dbParams[2].key, emptyAsNull(dbport));
340: } catch (NumberFormatException e) {
341: params.put(dbParams[2].key, "50000"); //$NON-NLS-1$
342: }
343: String db = getDBText();
344: params.put(dbParams[3].key, emptyAsNull(db));
345:
346: String userName = getUserText();
347: params.put(dbParams[4].key, emptyAsNull(userName));
348: String password = getPassText();
349: params.put(dbParams[5].key, emptyAsNull(password));
350:
351: params.put(dbParams[6].key, emptyAsNull(getSchema()));
352:
353: return params;
354: }
355:
356: /**
357: * This method does nothing.
358: * TODO: perhaps return a jdbc url?
359: */
360: public List<URL> getURLs() {
361: return null;
362: }
363:
364: protected String getPassText() {
365: return this .pass.getText();
366: }
367:
368: protected String getPortText() {
369: return this .port.getText();
370: }
371:
372: /**
373: * Creates the DB2 service so we can do real work. Saves the values of text fields from the GUI
374: * so that they can be used the next time this GUI page is displayed.
375: *
376: * @param monitor
377: * @return a List with the DB2 service
378: * @throws Exception
379: */
380: public List<IService> getResources(IProgressMonitor monitor)
381: throws Exception {
382: if (!isPageComplete())
383: return null;
384:
385: DB2ServiceExtension creator = new DB2ServiceExtension();
386: IService service = creator.createService(null, getParams());
387: service.getInfo(monitor); // load
388:
389: List<IService> servers = new ArrayList<IService>();
390: servers.add(service);
391:
392: /*
393: * Success! Store the connection settings in history.
394: */
395: saveWidgetValues();
396:
397: return servers;
398: }
399:
400: /**
401: * Gets the selected schema value.
402: *
403: * @return field text contents
404: */
405: protected String getSchema() {
406: if (schema == null)
407: return ""; //$NON-NLS-1$
408: return ((CCombo) schema).getText();
409: }
410:
411: /**
412: * Gets the user value.
413: *
414: * @return field text contents
415: */
416: protected String getUserText() {
417: return this .user.getText();
418: }
419:
420: /**
421: * DB2 always requires the schema.
422: *
423: * @return true
424: */
425: @Override
426: protected boolean hasSchema() {
427: return true;
428: }
429:
430: /**
431: * DB2 doesn't allow database name selection from a list.
432: *
433: * @return false
434: */
435: @Override
436: protected boolean isDBCombo() {
437: return false;
438: }
439:
440: /**
441: * DB2 allows a host selection list.
442: *
443: * @return true
444: */
445: @Override
446: protected boolean isHostCombo() {
447: return true;
448: }
449:
450: @Override
451: public boolean doIsPageComplete() {
452: boolean isComplete = false;
453: if (areDbFieldsFilled()) {
454: this .schema.setEnabled(true);
455: } else {
456: if (schema != null)
457: this .schema.setEnabled(false);
458: }
459: if (areAllFieldsFilled())
460: isComplete = factory.canProcess(getParams());
461: return isComplete;
462: }
463:
464: /**
465: * Fills the combo-box with the schema values available for the specified database. The DB2
466: * catalog table db2gse.st_geometry_columns is used to get a list of all the schema values
467: * associated with tables that have spatial columns.
468: */
469: @Override
470: protected void populateSchema() {
471: if (!hasSchema()) // error
472: return;
473:
474: // save some state
475: CCombo schemaCombo = this .schema;
476: int selected = schemaCombo.getSelectionIndex();
477: String string = null;
478: if (selected > -1) {
479: string = schemaCombo.getItem(selected);
480: }
481:
482: schemaCombo.removeAll();
483: schemaCombo.setText(""); //$NON-NLS-1$
484:
485: Connection con = null;
486: try {
487: con = getConnection();
488: } catch (Exception e) {
489: CatalogUIPlugin.log(e.getLocalizedMessage(), e);
490: setErrorMessage(e.getLocalizedMessage());
491: }
492:
493: if (con == null)
494: return;
495: String sqlStmt = "select distinct table_schema from db2gse.st_geometry_columns"; //$NON-NLS-1$
496:
497: ResultSet rs = null;
498: try {
499: Statement stmt = con.createStatement();
500: rs = stmt.executeQuery(sqlStmt);
501: while (rs.next()) {
502: String schemaName = rs.getString(1).trim();
503: if (!excludeSchema(schemaName))
504: schemaCombo.add(schemaName);
505: }
506: if (schemaCombo.getItemCount() > 0)
507: schemaCombo.select(0);
508:
509: } catch (SQLException e) {
510: setErrorMessage(e.getLocalizedMessage());
511: // e.printStackTrace();
512: // schema.removeAll();
513: // schema.setText(""); //$NON-NLS-1$
514: return;
515: }
516:
517: if (string != null) {
518: String[] items = schemaCombo.getItems();
519: for (int i = 0; items != null && i < items.length; i++) {
520: if (string.equals(items[i])) {
521: schemaCombo.select(i);
522: return;
523: }
524: }
525: }
526: }
527:
528: /**
529: * Saves the widget values
530: */
531: private void saveWidgetValues() {
532: // Update history
533: if (this .settings != null) {
534: String[] recentDB2s = this .settings.getArray(DB2_RECENT);
535: if (recentDB2s == null) {
536: recentDB2s = new String[0];
537: }
538: String dbs = new DataBaseConnInfo(getHostText(),
539: getPortText(), getUserText(), getPassText(),
540: getDBText(), getSchema()).toString();
541: recentDB2s = addToHistory(recentDB2s, dbs);
542: this .settings.put(DB2_RECENT, recentDB2s);
543: }
544: }
545:
546: /**
547: * Sets the database text field.
548: *
549: * @param value
550: */
551: protected void setDBText(String value) {
552: ((Text) DB2WizardPage.this .database).setText(value);
553: }
554:
555: /**
556: * Sets the password text field.
557: *
558: * @param value
559: */
560: protected void setPassText(String value) {
561: this .pass.setText(value);
562: }
563:
564: /**
565: * Sets the port text field.
566: *
567: * @param value
568: */
569: protected void setPortText(String value) {
570: this .port.setText(value);
571: }
572:
573: /**
574: * Sets the userid text field.
575: *
576: * @param value
577: */
578: protected void setUserText(String value) {
579: this .user.setText(value);
580: }
581:
582: @Override
583: protected void doCreateWizardPage(Composite parent) {
584:
585: this .port.setTextLimit(5);
586: this .port.setText("50000"); //$NON-NLS-1$
587: this .schema.setEnabled(false);
588:
589: String[] recentDB2s = this .settings.getArray(DB2_RECENT);
590: ArrayList<String> hosts = new ArrayList<String>();
591: this .dbData = new ArrayList<DataBaseConnInfo>();
592: if (recentDB2s != null) {
593: for (String recent : recentDB2s) {
594: DataBaseConnInfo dbs = new DataBaseConnInfo(recent);
595: this .dbData.add(dbs);
596: hosts.add(dbs.getHost());
597: }
598: }
599: if (hosts.size() > 0) {
600: ((CCombo) this .host).setItems(hosts.toArray(new String[0]));
601: ((CCombo) this .host)
602: .addModifyListener(new ModifyListener() {
603: public void modifyText(ModifyEvent e) {
604: if (e.widget != null) {
605: for (DataBaseConnInfo db : DB2WizardPage.this .dbData) {
606: if (db.getHost().equalsIgnoreCase(
607: getHostText())) {
608: setPortText(db.getPort());
609: setUserText(db.getUser());
610: setPassText(db.getPass());
611: setPassText(db.getPass());
612: setDBText(db.getDb());
613: DB2WizardPage.this .schema
614: .setText(db.getSchema());
615: break;
616: }
617: }
618: }
619: }
620: });
621: }
622: }
623:
624: @Override
625: protected String getDriversMessage() {
626: return Messages.DB2WizardPage_installDrivers;
627: }
628:
629: @Override
630: protected AbstractProprietaryJarPreferencePage getPreferencePage() {
631: return new DB2Preferences();
632: }
633:
634: @Override
635: protected String getRestartMessage() {
636: return Messages.DB2WizardPage_warning;
637: }
638:
639: }
|