001: /*
002: * ConnectionProfile.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.db;
013:
014: import java.awt.Color;
015: import java.beans.PropertyChangeListener;
016: import java.util.Comparator;
017: import java.util.Enumeration;
018: import java.util.Properties;
019:
020: import workbench.gui.profiles.ProfileKey;
021: import workbench.resource.ResourceMgr;
022: import workbench.resource.Settings;
023: import workbench.sql.DelimiterDefinition;
024: import workbench.util.StringUtil;
025: import workbench.util.WbCipher;
026: import workbench.util.WbDesCipher;
027: import workbench.util.WbPersistence;
028:
029: /**
030: * A class to store a connection definition including non-JDBC properties
031: * specific to the application.
032: * @author support@sql-workbench.net
033: */
034: public class ConnectionProfile implements PropertyChangeListener {
035: public static final String PROPERTY_PROFILE_GROUP = "profileGroup";
036: private static final String CRYPT_PREFIX = "@*@";
037: private String name;
038: private String url;
039: private String driverclass;
040: private String username;
041: private String password;
042: private String driverName;
043: private String group;
044: private boolean autocommit;
045: private boolean rollbackBeforeDisconnect;
046: private boolean changed;
047: private boolean isNew;
048: private boolean storePassword = true;
049: private boolean separateConnection;
050: private Properties connectionProperties;
051: private String workspaceFile;
052: private boolean ignoreDropErrors;
053: private boolean confirmUpdates;
054: private boolean trimCharData;
055: private Integer defaultFetchSize;
056:
057: private boolean emptyStringIsNull = false;
058: private boolean includeNullInInsert = true;
059: private boolean removeComments = false;
060: private boolean rememberExplorerSchema = false;
061: private String postConnectScript;
062: private String preDisconnectScript;
063: private String idleScript;
064: private long idleTime = 0;
065: private Color infoColor;
066: private boolean copyPropsToSystem = false;
067:
068: private DelimiterDefinition alternateDelimiter;
069:
070: static {
071: WbPersistence.makeTransient(ConnectionProfile.class,
072: "inputPassword");
073: WbPersistence.makeTransient(ConnectionProfile.class,
074: "useSeperateConnectionPerTab");
075: WbPersistence.makeTransient(ConnectionProfile.class,
076: "disableUpdateTableCheck");
077: }
078:
079: public ConnectionProfile() {
080: this .isNew = true;
081: this .changed = true;
082: Settings.getInstance().addPropertyChangeListener(this ,
083: Settings.PROPERTY_ENCRYPT_PWD);
084: }
085:
086: public ConnectionProfile(String aName, String driverClass,
087: String url, String userName, String pwd) {
088: this ();
089: this .setUrl(url);
090: this .setDriverclass(driverClass);
091: this .setUsername(userName);
092: this .setPassword(pwd);
093: this .setName(aName);
094: this .reset();
095: }
096:
097: public Color getInfoDisplayColor() {
098: return this .infoColor;
099: }
100:
101: public void setInfoDisplayColor(Color c) {
102: if (this .infoColor == null && c == null)
103: return;
104: if (this .infoColor != null && c != null) {
105: this .changed = !this .infoColor.equals(c);
106: } else {
107: this .changed = true;
108: }
109: this .infoColor = c;
110: }
111:
112: public void setAlternateDelimiter(DelimiterDefinition def) {
113: if (def == null && this .alternateDelimiter == null)
114: return;
115:
116: // Do not accept a semicolon as the alternate delimiter
117: if (def != null && def.isStandard())
118: return;
119:
120: if ((def == null && this .alternateDelimiter != null)
121: || (def != null && this .alternateDelimiter == null)
122: || (def != null && def.isChanged())) {
123: this .alternateDelimiter = def;
124: this .changed = true;
125: }
126: }
127:
128: public void setCopyExtendedPropsToSystem(boolean flag) {
129: if (flag != this .copyPropsToSystem) {
130: this .copyPropsToSystem = flag;
131: this .changed = true;
132: }
133: }
134:
135: public boolean getCopyExtendedPropsToSystem() {
136: return this .copyPropsToSystem;
137: }
138:
139: public boolean getTrimCharData() {
140: return trimCharData;
141: }
142:
143: public void setTrimCharData(boolean flag) {
144: changed = (flag != trimCharData);
145: trimCharData = flag;
146: }
147:
148: public DelimiterDefinition getAlternateDelimiter() {
149: if (this .alternateDelimiter == null)
150: return null;
151: if (this .alternateDelimiter.isEmpty())
152: return null;
153: return this .alternateDelimiter;
154: }
155:
156: public boolean getStoreExplorerSchema() {
157: return rememberExplorerSchema;
158: }
159:
160: public void setStoreExplorerSchema(boolean value) {
161: if (value != rememberExplorerSchema) {
162: rememberExplorerSchema = value;
163: changed = true;
164: }
165: }
166:
167: public String getGroup() {
168: if (this .group == null)
169: return ResourceMgr.getString("LblDefGroup");
170: return this .group;
171: }
172:
173: public void setGroup(String g) {
174: if (StringUtil.equalString(this .group, g))
175: return;
176: this .group = g;
177: this .changed = true;
178: }
179:
180: public boolean isProfileForKey(ProfileKey key) {
181: ProfileKey myKey = getKey();
182: return myKey.equals(key);
183: }
184:
185: public ProfileKey getKey() {
186: return new ProfileKey(this .getName(), this .getGroup());
187: }
188:
189: /**
190: * This method is used for backward compatibility. Old profiles
191: * had this property and to be able to load XML files with
192: * old profiles the setter must still be there.
193: *
194: * @deprecated
195: * @param flag
196: */
197: public void setDisableUpdateTableCheck(boolean flag) {
198: }
199:
200: /**
201: * Return true if the application should use a separate connection
202: * per tab or if all SQL tabs including DbExplorer tabs and windows
203: * should share the same connection
204: */
205: public boolean getUseSeparateConnectionPerTab() {
206: return this .separateConnection;
207: }
208:
209: public void setUseSeparateConnectionPerTab(boolean aFlag) {
210: if (this .separateConnection != aFlag)
211: this .changed = true;
212: this .separateConnection = aFlag;
213: }
214:
215: public void setIncludeNullInInsert(boolean flag) {
216: if (this .includeNullInInsert != flag)
217: this .changed = true;
218: this .includeNullInInsert = flag;
219: }
220:
221: /**
222: * Define how columns with a NULL value are treated when creating INSERT statements.
223: * If this is set to false, then any column with an a NULL value
224: * will not be included in an generated INSERT statement.
225: *
226: * @see workbench.storage.StatementFactory#createInsertStatement(workbench.storage.RowData, boolean, String, java.util.List)
227: */
228: public boolean getIncludeNullInInsert() {
229: return this .includeNullInInsert;
230: }
231:
232: /**
233: * Define how empty strings (Strings with length == 0) are treated.
234: * If this is set to true, then they are treated as a NULL value, else an
235: * empty string is sent to the database during update and insert.
236: * @see #setIncludeNullInInsert(boolean)
237: */
238: public void setEmptyStringIsNull(boolean flag) {
239: if (this .emptyStringIsNull != flag)
240: this .changed = true;
241: this .emptyStringIsNull = flag;
242: }
243:
244: public boolean getEmptyStringIsNull() {
245: return this .emptyStringIsNull;
246: }
247:
248: /**
249: * Define how comments inside SQL statements are handled.
250: * If this is set to true, then any comment (single line comments with --
251: * or multi-line comments using /* are removed from the statement
252: * before sending it to the database.
253: *
254: * @see workbench.sql.DefaultStatementRunner#runStatement(String, int, int)
255: */
256: public void setRemoveComments(boolean flag) {
257: if (this .removeComments != flag)
258: this .changed = true;
259: this .removeComments = flag;
260: }
261:
262: public boolean getRemoveComments() {
263: return this .removeComments;
264: }
265:
266: /**
267: * @deprecated Replaced by {@link #setUseSeparateConnectionPerTab(boolean)}
268: */
269: public void setUseSeperateConnectionPerTab(boolean aFlag) {
270: this .setUseSeparateConnectionPerTab(aFlag);
271: }
272:
273: public boolean getRollbackBeforeDisconnect() {
274: return this .rollbackBeforeDisconnect;
275: }
276:
277: public void setRollbackBeforeDisconnect(boolean flag) {
278: if (flag != this .rollbackBeforeDisconnect)
279: this .changed = true;
280: this .rollbackBeforeDisconnect = flag;
281: }
282:
283: /**
284: * Sets the current password. If the password
285: * is not already encrypted, it will be encrypted
286: *
287: * @see #getPassword()
288: * @see workbench.util.WbCipher#encryptString(String)
289: */
290: public void setPassword(String aPwd) {
291: if (aPwd == null) {
292: if (this .password != null) {
293: this .password = null;
294: this .changed = true;
295: }
296: return;
297: }
298:
299: aPwd = aPwd.trim();
300:
301: // check encryption settings when reading the profiles...
302: if (Settings.getInstance().getUseEncryption()) {
303: if (!this .isEncrypted(aPwd)) {
304: aPwd = this .encryptPassword(aPwd);
305: }
306: } else {
307: if (this .isEncrypted(aPwd)) {
308: aPwd = this .decryptPassword(aPwd);
309: }
310: }
311:
312: if (!aPwd.equals(this .password)) {
313: this .password = aPwd;
314: if (this .storePassword)
315: this .changed = true;
316: }
317: }
318:
319: /**
320: * Returns the encrypted version of the password.
321: * This getter/setter pair is used when saving the profile
322: * @see #decryptPassword(String)
323: */
324: public String getPassword() {
325: if (this .storePassword)
326: return this .password;
327: else
328: return null;
329: }
330:
331: /**
332: * Returns the user's password in plain readable text.
333: * (This value is send to the DB server)
334: */
335: public String getInputPassword() {
336: if (this .storePassword)
337: return this .decryptPassword();
338: else
339: return "";
340: }
341:
342: public void setInputPassword(String aPassword) {
343: this .setPassword(aPassword);
344: }
345:
346: /**
347: * Returns the plain text version of the
348: * current password.
349: *
350: * @see #encryptPassword(String)
351: */
352: public String decryptPassword() {
353: return this .decryptPassword(this .password);
354: }
355:
356: /**
357: * Returns the plain text version of the given
358: * password. This is not put into the getPassword()
359: * method because the XMLEncode would write the
360: * password in plain text into the XML file.
361: *
362: * A method beginning with decrypt is not
363: * regarded as a property and thus not written
364: * to the XML file.
365: *
366: * @param aPwd the encrypted password
367: */
368: public String decryptPassword(String aPwd) {
369: if (aPwd == null)
370: return null;
371: if (!isEncrypted(aPwd)) {
372: return aPwd;
373: } else {
374: WbCipher des = WbDesCipher.getInstance();
375: return des.decryptString(aPwd.substring(CRYPT_PREFIX
376: .length()));
377: }
378: }
379:
380: private boolean isEncrypted(String aPwd) {
381: return aPwd.startsWith(CRYPT_PREFIX);
382: }
383:
384: public void setNew() {
385: this .changed = true;
386: this .isNew = true;
387: }
388:
389: public boolean isNew() {
390: return this .isNew;
391: }
392:
393: public boolean isChanged() {
394: return this .changed || this .isNew;
395: }
396:
397: /**
398: * Reset the changed and new flags.
399: * @see #isNew()
400: * @see #isChanged()
401: */
402: public void reset() {
403: this .changed = false;
404: this .isNew = false;
405: if (this .alternateDelimiter != null)
406: this .alternateDelimiter.resetChanged();
407: }
408:
409: private String encryptPassword(String aPwd) {
410: if (Settings.getInstance().getUseEncryption()) {
411: if (!this .isEncrypted(aPwd)) {
412: WbCipher des = WbDesCipher.getInstance();
413: aPwd = CRYPT_PREFIX + des.encryptString(aPwd);
414: }
415: }
416: return aPwd;
417: }
418:
419: /**
420: * Returns the name of the Profile
421: */
422: public String toString() {
423: return this .name;
424: }
425:
426: /**
427: * The hashCode is based on the profile key's hash code.
428: *
429: * @see #getKey()
430: * @see ProfileKey#hashCode()
431: * @return the hashcode for the profile key
432: */
433: public int hashCode() {
434: return getKey().hashCode();
435: }
436:
437: public boolean equals(Object other) {
438: try {
439: ConnectionProfile prof = (ConnectionProfile) other;
440: return this .getKey().equals(prof.getKey());
441: } catch (ClassCastException e) {
442: return false;
443: }
444: }
445:
446: public String getUrl() {
447: return this .url;
448: }
449:
450: public void setUrl(String aUrl) {
451: if (aUrl != null)
452: aUrl = aUrl.trim();
453: this .url = aUrl;
454: this .changed = true;
455: }
456:
457: public String getDriverclass() {
458: return this .driverclass;
459: }
460:
461: public void setDriverclass(String aDriverclass) {
462: if (aDriverclass != null)
463: aDriverclass = aDriverclass.trim();
464: this .driverclass = aDriverclass;
465: this .changed = true;
466: }
467:
468: public String getUsername() {
469: return this .username;
470: }
471:
472: public void setUsername(java.lang.String aUsername) {
473: if (aUsername != null)
474: aUsername = aUsername.trim();
475: this .username = aUsername;
476: this .changed = true;
477: }
478:
479: public boolean getAutocommit() {
480: return this .autocommit;
481: }
482:
483: public void setAutocommit(boolean aFlag) {
484: if (aFlag != this .autocommit) {
485: this .autocommit = aFlag;
486: this .changed = true;
487: }
488: }
489:
490: public String getName() {
491: return this .name;
492: }
493:
494: public void setName(String aName) {
495: this .changed = !StringUtil.equalString(name, aName);
496: this .name = aName;
497: }
498:
499: public boolean getStorePassword() {
500: return this .storePassword;
501: }
502:
503: public void setStorePassword(boolean aFlag) {
504: this .storePassword = aFlag;
505: }
506:
507: public ConnectionProfile createCopy() {
508: ConnectionProfile result = new ConnectionProfile();
509: result.setAutocommit(autocommit);
510: result.setDriverclass(driverclass);
511: result.setDriverName(driverName);
512: result.setName(name);
513: result.setGroup(group);
514: result.setPassword(getPassword());
515: result.setUrl(url);
516: result.setUsername(username);
517: result.setWorkspaceFile(workspaceFile);
518: result.setIgnoreDropErrors(ignoreDropErrors);
519: result.setUseSeparateConnectionPerTab(separateConnection);
520: result.setTrimCharData(trimCharData);
521: result.setIncludeNullInInsert(includeNullInInsert);
522: result.setEmptyStringIsNull(emptyStringIsNull);
523: result.setRollbackBeforeDisconnect(rollbackBeforeDisconnect);
524: result.setConfirmUpdates(confirmUpdates);
525: result.setStorePassword(storePassword);
526: result.setDefaultFetchSize(defaultFetchSize);
527: result.setStoreExplorerSchema(rememberExplorerSchema);
528: result.setIdleScript(idleScript);
529: result.setIdleTime(idleTime);
530: result.setPreDisconnectScript(preDisconnectScript);
531: result.setPostConnectScript(postConnectScript);
532: result.setInfoDisplayColor(infoColor);
533: result.setAlternateDelimiter(alternateDelimiter == null ? null
534: : alternateDelimiter.createCopy());
535:
536: result.setCopyExtendedPropsToSystem(copyPropsToSystem);
537: if (connectionProperties != null) {
538: Enumeration keys = connectionProperties.propertyNames();
539: result.connectionProperties = new Properties();
540:
541: while (keys.hasMoreElements()) {
542: String key = (String) keys.nextElement();
543: String value = connectionProperties.getProperty(key);
544: result.connectionProperties.put(key, value);
545: }
546: }
547:
548: return result;
549: }
550:
551: public static Comparator<ConnectionProfile> getNameComparator() {
552: return new Comparator<ConnectionProfile>() {
553: public int compare(ConnectionProfile o1,
554: ConnectionProfile o2) {
555: if (o1 == null && o2 == null)
556: return 0;
557: if (o1 == null)
558: return -1;
559: if (o2 == null)
560: return 1;
561: return StringUtil
562: .compareStrings(o1.name, o2.name, true);
563: }
564: };
565: }
566:
567: public boolean getIgnoreDropErrors() {
568: return this .ignoreDropErrors;
569: }
570:
571: public void setIgnoreDropErrors(boolean aFlag) {
572: this .changed = (aFlag != this .ignoreDropErrors);
573: this .ignoreDropErrors = aFlag;
574: }
575:
576: public String getWorkspaceFile() {
577: return this .workspaceFile;
578: }
579:
580: public void setWorkspaceFile(String aWorkspaceFile) {
581: this .workspaceFile = aWorkspaceFile;
582: this .changed = true;
583: }
584:
585: public void addConnectionProperty(String aKey, String aValue) {
586: if (aKey == null)
587: return;
588: if (this .connectionProperties == null) {
589: this .connectionProperties = new Properties();
590: }
591: this .connectionProperties.put(aKey, aValue);
592: this .changed = true;
593: }
594:
595: public Properties getConnectionProperties() {
596: return this .connectionProperties;
597: }
598:
599: public void setConnectionProperties(Properties props) {
600: boolean wasDefined = (this .connectionProperties != null && this .connectionProperties
601: .size() > 0);
602: if (props != null) {
603: if (props.size() == 0) {
604: this .connectionProperties = null;
605: if (wasDefined)
606: this .changed = true;
607: } else {
608: this .connectionProperties = props;
609: this .changed = true;
610: }
611: } else {
612: this .connectionProperties = null;
613: if (wasDefined)
614: this .changed = true;
615: }
616: }
617:
618: public java.lang.String getDriverName() {
619: return driverName;
620: }
621:
622: public void setDriverName(java.lang.String driverName) {
623: this .driverName = driverName;
624: }
625:
626: public boolean isConfirmUpdates() {
627: return confirmUpdates;
628: }
629:
630: public void setConfirmUpdates(boolean flag) {
631: if (flag != this .confirmUpdates)
632: this .changed = true;
633: this .confirmUpdates = flag;
634: }
635:
636: public void propertyChange(java.beans.PropertyChangeEvent evt) {
637: if (Settings.PROPERTY_ENCRYPT_PWD.equals(evt.getPropertyName())) {
638: String old = this .password;
639: // calling setPassword will encrypt/decrypt the password
640: // according to the current setting
641: this .setPassword(old);
642: }
643: }
644:
645: public int getFetchSize() {
646: if (this .defaultFetchSize == null)
647: return -1;
648: else
649: return this .defaultFetchSize.intValue();
650: }
651:
652: public Integer getDefaultFetchSize() {
653: return defaultFetchSize;
654: }
655:
656: public void setDefaultFetchSize(Integer fetchSize) {
657: int currentValue = (defaultFetchSize == null ? Integer.MIN_VALUE
658: : defaultFetchSize.intValue());
659: int newValue = (fetchSize == null ? Integer.MIN_VALUE
660: : fetchSize.intValue());
661:
662: if (currentValue != newValue && newValue > -1) {
663: this .defaultFetchSize = fetchSize;
664: this .changed = true;
665: }
666: }
667:
668: public String getPostConnectScript() {
669: return postConnectScript;
670: }
671:
672: public void setPostConnectScript(String script) {
673: if (!StringUtil.equalStringOrEmpty(script,
674: this .postConnectScript)) {
675: if (StringUtil.isEmptyString(script)
676: || script.trim().length() == 0) {
677: this .postConnectScript = null;
678: } else {
679: this .postConnectScript = script.trim();
680: }
681: this .changed = true;
682: }
683: }
684:
685: public String getPreDisconnectScript() {
686: return preDisconnectScript;
687: }
688:
689: public void setPreDisconnectScript(String script) {
690: if (!StringUtil.equalStringOrEmpty(script,
691: this .preDisconnectScript)) {
692: if (StringUtil.isEmptyString(script)
693: || script.trim().length() == 0) {
694: this .preDisconnectScript = null;
695: } else {
696: this .preDisconnectScript = script.trim();
697: }
698: this .changed = true;
699: }
700: }
701:
702: public long getIdleTime() {
703: return this .idleTime;
704: }
705:
706: public void setIdleTime(long time) {
707: if (time != this .idleTime) {
708: this .idleTime = time;
709: this .changed = true;
710: }
711: }
712:
713: public String getIdleScript() {
714: return idleScript;
715: }
716:
717: public void setIdleScript(String script) {
718: if (!StringUtil.equalStringOrEmpty(script, this .idleScript)) {
719: if (StringUtil.isEmptyString(script)
720: || script.trim().length() == 0) {
721: this .idleScript = null;
722: } else {
723: this .idleScript = script.trim();
724: }
725: this .changed = true;
726: }
727: }
728:
729: }
|