Refactored methods in VersionableDaoBase etc + added Query-By-Example for CdmEntityDa...
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / database / CdmPersistentDataSource.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.database;
11
12 import static eu.etaxonomy.cdm.common.XmlHelp.getBeansRoot;
13 import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlBean;
14 import static eu.etaxonomy.cdm.common.XmlHelp.insertXmlValueProperty;
15 import static eu.etaxonomy.cdm.common.XmlHelp.saveToXml;
16
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Enumeration;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Properties;
27
28 import javax.sql.DataSource;
29
30 import org.apache.commons.dbcp.BasicDataSource;
31 import org.apache.log4j.Logger;
32 import org.hibernate.cache.CacheProvider;
33 import org.hibernate.cache.NoCacheProvider;
34 import org.jdom.Attribute;
35 import org.jdom.Document;
36 import org.jdom.Element;
37 import org.jdom.output.Format;
38 import org.springframework.beans.MutablePropertyValues;
39 import org.springframework.beans.factory.config.BeanDefinition;
40 import org.springframework.beans.factory.config.PropertiesFactoryBean;
41 import org.springframework.beans.factory.support.AbstractBeanDefinition;
42 import org.springframework.beans.factory.support.RootBeanDefinition;
43
44 import eu.etaxonomy.cdm.api.application.CdmApplicationUtils;
45 import eu.etaxonomy.cdm.common.CdmUtils;
46 import eu.etaxonomy.cdm.common.XmlHelp;
47 import eu.etaxonomy.cdm.database.types.IDatabaseType;
48 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
49
50
51 /**
52 * class to access an CdmDataSource
53 */
54 public class CdmPersistentDataSource extends CdmDataSourceBase{
55 private static final Logger logger = Logger.getLogger(CdmPersistentDataSource.class);
56
57 public static final String DATASOURCE_BEAN_POSTFIX = "DataSource";
58 public final static String DATASOURCE_FILE_NAME = "cdm.datasources.xml";
59 public final static String DATASOURCE_PATH = "/eu/etaxonomy/cdm/";
60
61 private final static Format format = Format.getPrettyFormat();
62
63 public enum DbProperties{
64 DRIVER_CLASS,
65 URL,
66 SERVER,
67 DATABASE,
68 PORT,
69 MODE,
70 FILEPATH,
71 USERNAME,
72 PASSWORD,
73 NOMENCLATURAL_CODE;
74
75 @Override
76 public String toString(){
77 switch (this){
78 case DRIVER_CLASS:
79 return "driverClassName";
80 case URL:
81 return "url";
82 case SERVER:
83 return "server";
84 case DATABASE:
85 return "database";
86 case PORT:
87 return "port";
88 case MODE:
89 return "mode";
90 case FILEPATH:
91 return "filePath";
92 case USERNAME:
93 return "username";
94 case PASSWORD:
95 return "password";
96 case NOMENCLATURAL_CODE:
97 return "nomenclaturalCode";
98 default:
99 throw new IllegalArgumentException( "Unknown enumeration type" );
100 }
101 }
102 }
103
104
105 //name
106 protected String dataSourceName;
107
108
109 /**
110 * Returns the default CdmDataSource
111 * @return the default CdmDataSource
112 */
113 public final static CdmPersistentDataSource NewDefaultInstance(){
114 try {
115 return NewInstance("default");
116 } catch (DataSourceNotFoundException e) {
117 logger.error("Default datasource does not exist in config file");
118 return null;
119 }
120 }
121
122
123 /**
124 * Returns the default CdmDataSource
125 * @return the default CdmDataSource
126 */
127 public final static CdmPersistentDataSource NewLocalHsqlInstance(){
128 try {
129 return NewInstance("localDefaultHsql");
130 } catch (DataSourceNotFoundException e) {
131 logger.error("Local datasource does not exist in config file");
132 return null;
133 }
134 }
135
136 /**
137 * Returns the CdmDataSource named by strDataSource
138 * @param strDataSource
139 * @return
140 */
141 public final static CdmPersistentDataSource NewInstance(String dataSourceName)
142 throws DataSourceNotFoundException{
143 if (exists(dataSourceName)){
144 return new CdmPersistentDataSource(dataSourceName);
145 }else{
146 throw new DataSourceNotFoundException("Datasource not found: " + dataSourceName);
147 }
148 }
149
150 /**
151 * Private Constructor. Use NewXXX factory methods for creating a new instance of CdmDataSource!
152 * @param strDataSource
153 */
154 private CdmPersistentDataSource(String strDataSource){
155 dataSourceName = strDataSource;
156 }
157
158 /**
159 * Returns the name of the bean.
160 * @return
161 */
162 public String getName(){
163 return dataSourceName;
164 }
165
166
167 /**
168 * Returns the name of the bean Element in the xml config file.
169 * @return bean name
170 */
171 private static String getBeanName(String name){
172 return name == null? null : name + DATASOURCE_BEAN_POSTFIX;
173 }
174
175
176
177 public String getDatabase() {
178 return getDatabaseProperty(DbProperties.DATABASE);
179 }
180
181
182 public String getFilePath() {
183 //TODO null
184 return getDatabaseProperty(DbProperties.FILEPATH);
185 }
186
187
188 public H2Mode getMode() {
189 //TODO null
190 return H2Mode.fromString(getDatabaseProperty(DbProperties.MODE));
191 }
192
193
194 /* (non-Javadoc)
195 * @see eu.etaxonomy.cdm.database.ICdmDataSource#getNomenclaturalCode()
196 */
197 public NomenclaturalCode getNomenclaturalCode() {
198 // TODO null
199 return NomenclaturalCode.fromString(getDatabaseProperty(DbProperties.NOMENCLATURAL_CODE));
200 }
201
202 public int getPort() {
203 String port = CdmUtils.Nz(getDatabaseProperty(DbProperties.PORT));
204 if ("".equals(port)){
205 return -1;
206 }else{
207 //TODO exception if non integer
208 return Integer.valueOf(port);
209 }
210 }
211
212
213 public String getServer() {
214 return getDatabaseProperty(DbProperties.SERVER);
215 }
216
217 /**
218 * Returns the database type of the data source.
219 * @return the database type of the data source. Null if the bean or the driver class property does not exist or the driver class is unknown.
220 */
221 public DatabaseTypeEnum getDatabaseType(){
222 Element bean = getDatasourceBeanXml(this.dataSourceName);
223 if (bean == null){
224 return null;
225 }else{
226 Element driverProp = XmlHelp.getFirstAttributedChild(bean, "property", "name", "driverClassName");
227 if (driverProp == null){
228 logger.warn("Unknown property driverClass");
229 return null;
230 }else{
231 String strDriverClass = driverProp.getAttributeValue("value");
232 DatabaseTypeEnum dbType = DatabaseTypeEnum.getDatabaseEnumByDriverClass(strDriverClass);
233 return dbType;
234 }
235 }
236 }
237
238
239 /**
240 * Returns the database type of the data source.
241 * @return the database type of the data source. Null if the bean or the driver class property does not exist or the driver class is unknown.
242 */
243 protected String getDatabaseProperty(DbProperties property){
244 Element bean = getDatasourceBeanXml(this.dataSourceName);
245 String url;
246 String result = null;
247 if (bean != null){
248 result = getPropertyValue(bean, property.toString());
249 if (result == null){ //test if property is database, server or port which are included in the url
250 url = getPropertyValue(bean, DbProperties.URL.toString());
251 DatabaseTypeEnum dbTypeEnum = getDatabaseType();
252 if (dbTypeEnum != null){
253 IDatabaseType dbType = dbTypeEnum.getDatabaseType();
254 if (property.equals(DbProperties.DATABASE)){
255 result = dbType.getDatabaseNameByConnectionString(url);
256 }else if(property.equals(DbProperties.SERVER)){
257 result = dbType.getServerNameByConnectionString(url);
258 }else if(property.equals(DbProperties.PORT)){
259 result = String.valueOf(dbType.getPortByConnectionString(url));
260 }else{
261 logger.debug("Unknown property: " + property);
262 }
263 }
264 }
265 }
266 return result;
267 }
268
269 private String getPropertyValue(Element bean, String property){
270 Element driverProp = XmlHelp.getFirstAttributedChild(bean, "property", "name", property);
271 if (driverProp == null){
272 logger.debug("Unknown property: " + property);
273 return null;
274 }else{
275 String strProperty = driverProp.getAttributeValue("value");
276 return strProperty;
277 }
278 }
279
280
281
282 /**
283 * Returns the list of properties that are defined in the datasource
284 * @return
285 */
286 @SuppressWarnings("unchecked")
287 public List<Attribute> getDatasourceAttributes(){
288 List<Attribute> result = new ArrayList<Attribute>();
289 Element bean = getDatasourceBeanXml(this.dataSourceName);
290 if (bean == null){
291 return null;
292 }else{
293 result = bean.getAttributes();
294 }
295 return result;
296 }
297
298 /**
299 * Returns a defined property of the datasource
300 * @return the property of the data source. NULL if the datasource bean or the property does not exist.
301 */
302 public String getDatasourceProperty(DbProperties dbProp){
303 Element bean = getDatasourceBeanXml(this.dataSourceName);
304 if (bean == null){
305 return null;
306 }else{
307 Element elProperty = XmlHelp.getFirstAttributedChild(bean, "property", "name", dbProp.toString());
308 if (elProperty == null){
309 logger.warn("Unknown property: " + dbProp.toString());
310 return null;
311 }else{
312 String strValue = elProperty.getAttributeValue("value");
313 return strValue;
314 }
315 }
316 }
317
318
319 /**
320 * Returns the list of properties that are defined in the datasource
321 * @return
322 */
323 public Properties getDatasourceProperties(){
324 Properties result = new Properties();
325 Element bean = getDatasourceBeanXml(this.dataSourceName);
326 if (bean == null){
327 return null;
328 }else{
329 List<Element> elProperties = XmlHelp.getAttributedChildList(bean, "property", "name");
330 Iterator<Element> iterator = elProperties.iterator();
331 while(iterator.hasNext()){
332 Element next = iterator.next();
333 String strName = next.getAttributeValue("name");
334 String strValue = next.getAttributeValue("value");
335 result.put(strName, strValue);
336 }
337 }
338 return result;
339 }
340
341 /**
342 * Returns a BeanDefinition object of type DataSource that contains
343 * datsource properties (url, username, password, ...)
344 * @return
345 */
346 @SuppressWarnings("unchecked")
347 public BeanDefinition getDatasourceBean(){
348 DatabaseTypeEnum dbtype = DatabaseTypeEnum.getDatabaseEnumByDriverClass(getDatasourceProperty(DbProperties.DRIVER_CLASS));
349
350 AbstractBeanDefinition bd = new RootBeanDefinition(dbtype.getDataSourceClass());
351 //attributes
352 Iterator<Attribute> iterator = getDatasourceAttributes().iterator();
353 while(iterator.hasNext()){
354 Attribute attribute = iterator.next();
355 if (attribute.getName().equals("lazy-init")){
356 bd.setLazyInit(Boolean.valueOf(attribute.getValue()));
357 }
358 if (attribute.getName().equals("init-method")){
359 bd.setInitMethodName(attribute.getValue());
360 }
361 if (attribute.getName().equals("destroy-method")){
362 bd.setDestroyMethodName(attribute.getValue());
363 }
364 //Attribute attribute = iterator.next();
365 //bd.setAttribute(attribute.getName(), attribute.getValue());
366 }
367
368 //properties
369 MutablePropertyValues props = new MutablePropertyValues();
370 Properties persistentProperties = getDatasourceProperties();
371 Enumeration<String> keys = (Enumeration)persistentProperties.keys();
372 while (keys.hasMoreElements()){
373 String key = (String)keys.nextElement();
374 props.addPropertyValue(key, persistentProperties.getProperty(key));
375 }
376
377 bd.setPropertyValues(props);
378 return bd;
379 }
380
381 /**
382 * @param hbm2dll
383 * @param showSql
384 * @return
385 */
386 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll){
387 boolean showSql = false;
388 boolean formatSql = false;
389 boolean registerSearchListener = false;
390 Class<? extends CacheProvider> cacheProviderClass = NoCacheProvider.class;
391 return getHibernatePropertiesBean(hbm2dll, showSql, formatSql, registerSearchListener, cacheProviderClass);
392 }
393
394
395 /**
396 * @param hbm2dll
397 * @param showSql
398 * @return
399 */
400 public BeanDefinition getHibernatePropertiesBean(DbSchemaValidation hbm2dll, Boolean showSql, Boolean formatSql, Boolean registerSearchListener, Class<? extends CacheProvider> cacheProviderClass){
401 //Hibernate default values
402 if (hbm2dll == null){
403 hbm2dll = DbSchemaValidation.VALIDATE;
404 }
405 if (showSql == null){
406 showSql = false;
407 }
408 if (formatSql == null){
409 formatSql = false;
410 }
411 if (cacheProviderClass == null){
412 cacheProviderClass = NoCacheProvider.class;
413 }
414 if(registerSearchListener == null){
415 registerSearchListener = false;
416 }
417
418 DatabaseTypeEnum dbtype = getDatabaseType();
419 AbstractBeanDefinition bd = new RootBeanDefinition(PropertiesFactoryBean.class);
420 MutablePropertyValues hibernateProps = new MutablePropertyValues();
421
422 Properties props = new Properties();
423 props.setProperty("hibernate.hbm2ddl.auto", hbm2dll.toString());
424 props.setProperty("hibernate.dialect", dbtype.getHibernateDialect());
425 props.setProperty("hibernate.cache.provider_class", cacheProviderClass.getName());
426 props.setProperty("hibernate.show_sql", String.valueOf(showSql));
427 props.setProperty("hibernate.format_sql", String.valueOf(formatSql));
428 props.setProperty("hibernate.search.autoregister_listeners", String.valueOf(registerSearchListener));
429
430 hibernateProps.addPropertyValue("properties",props);
431 bd.setPropertyValues(hibernateProps);
432 return bd;
433 }
434
435
436 /**
437 * Tests existing of the datsource in the according config file.
438 * @return true if a datasource with the given name exists in the according datasource config file.
439 */
440 public static boolean exists(String strDataSourceName){
441 Element bean = getDatasourceBeanXml(strDataSourceName);
442 return (bean != null);
443 }
444
445
446 /**
447 *
448 * @param strDataSourceName
449 * @param databaseTypeEnum
450 * @param server
451 * @param database
452 * @param port
453 * @param username
454 * @param password
455 * @param dataSourceClass
456 * @param initMethod
457 * @param destroyMethod
458 * @param startSilent
459 * @param startServer
460 * @param filePath
461 * @param mode
462 * @return
463 */
464 private static CdmPersistentDataSource save(String strDataSourceName,
465 DatabaseTypeEnum databaseTypeEnum,
466 String server,
467 String database,
468 String port,
469 String username,
470 String password,
471 Class<? extends DataSource> dataSourceClass,
472 String initMethod,
473 String destroyMethod,
474 Boolean startSilent,
475 Boolean startServer,
476 String filePath,
477 H2Mode mode,
478 NomenclaturalCode code
479 ){
480
481 int portNumber = "".equals(port) ? databaseTypeEnum.getDefaultPort() : Integer.valueOf(port);
482
483 ICdmDataSource dataSource = new CdmDataSource(databaseTypeEnum, server, database, portNumber, username, password, filePath, mode, code);
484
485 //root
486 Element root = getBeansRoot(getDataSourceInputStream());
487 if (root == null){
488 return null;
489 }
490 //bean
491 Element bean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
492 if (bean != null){
493 bean.detach(); //delete old version if necessary
494 }
495 bean = insertXmlBean(root, getBeanName(strDataSourceName), dataSourceClass.getName());
496 //attributes
497 bean.setAttribute("lazy-init", "true");
498 if (initMethod != null) {bean.setAttribute("init-method", initMethod);}
499 if (destroyMethod != null) {bean.setAttribute("destroy-method", destroyMethod);}
500
501 //set properties
502 insertXmlValueProperty(bean, "driverClassName", databaseTypeEnum.getDriverClassName());
503
504 insertXmlValueProperty(bean, "url", databaseTypeEnum.getConnectionString(dataSource));
505 if (username != null) {insertXmlValueProperty(bean, "username", username );}
506 if (password != null) {insertXmlValueProperty(bean, "password", password );}
507 if (startSilent != null) {insertXmlValueProperty(bean, "startSilent", startSilent.toString() );}
508 if (startServer != null) {insertXmlValueProperty(bean, "startServer", startServer.toString() );}
509 if (filePath != null) {insertXmlValueProperty(bean, "filePath", filePath );}
510 if (mode != null) {insertXmlValueProperty(bean, "mode", mode.toString() );}
511 if (code != null) {insertXmlValueProperty(bean, "nomenclaturalCode", code.name());}
512
513 //save
514 saveToXml(root.getDocument(), getResourceDirectory(), DATASOURCE_FILE_NAME, format );
515 try {
516 return NewInstance(strDataSourceName) ;
517 } catch (DataSourceNotFoundException e) {
518 logger.error("Error when saving datasource");
519 return null;
520 }
521 }
522
523 /**
524 * @param strDataSourceName
525 * @param dataSource
526 * @param code
527 * @return
528 * the updated dataSource, null if not succesful
529 */
530 public static CdmPersistentDataSource update(String strDataSourceName,
531 ICdmDataSource dataSource) throws DataSourceNotFoundException, IllegalArgumentException{
532 delete(CdmPersistentDataSource.NewInstance(strDataSourceName));
533 return save(strDataSourceName, dataSource);
534 }
535
536 /**
537 * Saves a datasource to the datasource config file. If strDataSourceName differs a new dataSource
538 * will be created in config file. Use update() of real update functionality.
539 *
540 * @param strDataSourceName
541 * @param dataSource
542 * @return
543 */
544 public static CdmPersistentDataSource save(String strDataSourceName,
545 ICdmDataSource dataSource) throws IllegalArgumentException{
546
547 if(dataSource.getDatabaseType() == null){
548 new IllegalArgumentException("Database type not specified");
549 }
550
551 if(dataSource.getDatabaseType().equals(DatabaseTypeEnum.H2)){
552 Class<? extends DataSource> dataSourceClass = LocalH2.class;
553 if(dataSource.getMode() == null){
554 new IllegalArgumentException("H2 mode not specified");
555 }
556 return save(
557 strDataSourceName,
558 dataSource.getDatabaseType(),
559 "localhost",
560 getCheckedDataSourceParameter(dataSource.getDatabase()),
561 dataSource.getDatabaseType().getDefaultPort() + "",
562 getCheckedDataSourceParameter(dataSource.getUsername()),
563 getCheckedDataSourceParameter(dataSource.getPassword()),
564 dataSourceClass,
565 null, null, null, null,
566 getCheckedDataSourceParameter(dataSource.getFilePath()),
567 dataSource.getMode(),
568 dataSource.getNomenclaturalCode());
569 }else{
570 Class<? extends DataSource> dataSourceClass = BasicDataSource.class; //TODO make configurable
571
572 return save(
573 strDataSourceName,
574 dataSource.getDatabaseType(),
575 getCheckedDataSourceParameter(dataSource.getServer()),
576 getCheckedDataSourceParameter(dataSource.getDatabase()),
577 dataSource.getPort() + "",
578 getCheckedDataSourceParameter(dataSource.getUsername()),
579 getCheckedDataSourceParameter(dataSource.getPassword()),
580 dataSourceClass,
581 null, null, null, null, null, null,
582 dataSource.getNomenclaturalCode());
583 }
584 }
585
586 private static String getCheckedDataSourceParameter(String parameter) throws IllegalArgumentException{
587 if(parameter != null){
588 return parameter;
589 }else{
590 new IllegalArgumentException("Non obsolete paramater was assigned a null value: " + parameter);
591 return null;
592 }
593 }
594
595 /**
596 * Deletes a dataSource
597 * @param dataSource
598 */
599 public static void delete (CdmPersistentDataSource dataSource){
600 Element bean = getDatasourceBeanXml(dataSource.getName());
601 if (bean != null){
602 Document doc = bean.getDocument();
603 bean.detach();
604 saveToXml(doc, getDataSourceOutputStream(), format );
605 }
606 }
607
608
609 /**
610 * Returns a list of all datasources stored in the datasource config file
611 * @return all existing data sources
612 */
613 @SuppressWarnings("unchecked")
614 static public List<CdmPersistentDataSource> getAllDataSources(){
615 List<CdmPersistentDataSource> dataSources = new ArrayList<CdmPersistentDataSource>();
616
617 Element root = getBeansRoot(getDataSourceInputStream());
618 if (root == null){
619 return null;
620 }else{
621 List<Element> lsChildren = root.getChildren("bean", root.getNamespace());
622
623 for (Element elBean : lsChildren){
624 String strId = elBean.getAttributeValue("id");
625 if (strId != null && strId.endsWith(DATASOURCE_BEAN_POSTFIX)){
626 strId = strId.replace(DATASOURCE_BEAN_POSTFIX, "");
627 dataSources.add(new CdmPersistentDataSource(strId));
628 }
629 }
630 }
631 return dataSources;
632 }
633
634 public String getUsername(){
635 return getDatasourceProperty(DbProperties.USERNAME);
636 }
637
638 public String getPassword(){
639 return getDatasourceProperty(DbProperties.PASSWORD);
640 }
641
642
643 /* (non-Javadoc)
644 * @see java.lang.Object#toString()
645 */
646 public String toString(){
647 if (this.dataSourceName != null){
648 return dataSourceName;
649 }else{
650 return null;
651 }
652 }
653
654
655
656 /**
657 * Returns the datasource config file input stream.
658 * @return data source config file input stream
659 */
660 static protected FileInputStream getDataSourceInputStream(){
661 String dir = getResourceDirectory();
662 File file = new File(dir + File.separator + DATASOURCE_FILE_NAME);
663 return fileInputStream(file);
664 }
665
666
667 /**
668 * Returns the datasource config file outputStream.
669 * @return data source config file outputStream
670 */
671 static protected FileOutputStream getDataSourceOutputStream(){
672 String dir = getResourceDirectory();
673 File file = new File(dir + File.separator + DATASOURCE_FILE_NAME);
674 return fileOutputStream(file);
675 }
676
677 /**
678 * Returns the jdom Element representing the data source bean in the config file.
679 * @return
680 */
681 private static Element getDatasourceBeanXml(String strDataSourceName){
682 FileInputStream inStream = getDataSourceInputStream();
683 Element root = getBeansRoot(inStream);
684 if (root == null){
685 return null;
686 }else{
687 Element xmlBean = XmlHelp.getFirstAttributedChild(root, "bean", "id", getBeanName(strDataSourceName));
688 if (xmlBean == null){
689 //TODO warn or info
690 logger.debug("Unknown Element 'bean id=" +strDataSourceName + "' ");
691 };
692 return xmlBean;
693 }
694 }
695
696 // returns the directory containing the resources
697 private static String getResourceDirectory(){
698 try {
699 File f = CdmApplicationUtils.getWritableResourceDir();
700 return f.getPath();
701 } catch (IOException e) {
702 logger.error(e);
703 throw new RuntimeException(e);
704 }
705 }
706
707 static private FileInputStream fileInputStream(File file){
708 try {
709 FileInputStream fis = new FileInputStream(file);
710 return fis;
711 } catch (FileNotFoundException e) {
712 logger.warn("File " + file == null?"null":file.getAbsolutePath() + " does not exist in the file system");
713 return null;
714 }
715 }
716
717 static private FileOutputStream fileOutputStream(File file){
718 try {
719 FileOutputStream fos = new FileOutputStream(file);
720 return fos;
721 } catch (FileNotFoundException e) {
722 logger.warn("File " + (file == null?"null":file.getAbsolutePath()) + " does not exist in the file system");
723 return null;
724 }
725 }
726
727 public boolean equals(Object obj){
728 if (obj == null){
729 return false;
730 }else if (! CdmPersistentDataSource.class.isAssignableFrom(obj.getClass())){
731 return false;
732 }else{
733 CdmPersistentDataSource dataSource = (CdmPersistentDataSource)obj;
734 return (this.dataSourceName == dataSource.dataSourceName);
735 }
736
737 }
738 }