Coverage Report - org.apache.ojb.broker.metadata.DescriptorRepository
 
Classes in this File Line Coverage Branch Coverage Complexity
DescriptorRepository
N/A
N/A
3.455
 
 1  
 package org.apache.ojb.broker.metadata;
 2  
 
 3  
 /* Copyright 2002-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import java.io.Serializable;
 19  
 import java.util.*;
 20  
 
 21  
 import org.apache.commons.lang.SystemUtils;
 22  
 import org.apache.commons.lang.builder.ToStringBuilder;
 23  
 import org.apache.commons.lang.builder.ToStringStyle;
 24  
 import org.apache.ojb.broker.OJBRuntimeException;
 25  
 import org.apache.ojb.broker.PersistenceBrokerException;
 26  
 import org.apache.ojb.broker.locking.IsolationLevels;
 27  
 import org.apache.ojb.broker.util.ClassHelper;
 28  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 29  
 import org.apache.ojb.broker.util.logging.Logger;
 30  
 
 31  
 /**
 32  
  * The repository containing all object mapping and manipulation information of
 33  
  * all used persistent objects.
 34  
  * <br>
 35  
  * Note: Be careful when use references of this class or caching instances of this class,
 36  
  * because instances could become invalid (see {@link MetadataManager}).
 37  
  *
 38  
  * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
 39  
  * @author <a href="mailto:leandro@ibnetwork.com.br">Leandro Rodrigo Saad Cruz<a>
 40  
  * @version $Id: DescriptorRepository.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
 41  
  */
 42  
 @SuppressWarnings("unchecked")
 43  
 public final class DescriptorRepository extends DescriptorBase
 44  
         implements Serializable, XmlCapable, IsolationLevels
 45  
 {
 46  
     static final long serialVersionUID = -1556339982311359524L;
 47  
     private Logger log = LoggerFactory.getLogger(DescriptorRepository.class);
 48  
 
 49  
     /**
 50  
      * The version identifier of the Repository.
 51  
      * Used to validate repository.xml against the dtd.
 52  
      */
 53  
     private static final String VERSION = "1.0";
 54  
     /**
 55  
      * the default isolation level used for this repository
 56  
      */
 57  
     private int defaultIsolationLevel = IsolationLevels.IL_DEFAULT;
 58  
     /**
 59  
      * This table holds all known Mapping descriptions.
 60  
      * Key values are the respective Class objects
 61  
      */
 62  
         private final HashMap descriptorTable;
 63  
     /**
 64  
      * We need a lot the extent, to which a class belongs
 65  
      * (@see DescriptorRepository#getExtentClass). To speed up the costy
 66  
      * evaluation, we use this tiny hash map.
 67  
      */
 68  
     private Map extentTable;
 69  
 
 70  
     private Map superClassMultipleJoinedTablesMap;
 71  
 
 72  
     private transient Map m_multiMappedTableMap;
 73  
     private transient Map m_topLevelClassTable;
 74  
     private transient Map m_firstConcreteClassMap;
 75  
     private transient Map m_allConcreteSubClass;
 76  
 
 77  
     /**
 78  
      * Constructor declaration
 79  
      */
 80  
     public DescriptorRepository() throws PersistenceBrokerException
 81  
     {
 82  
         descriptorTable = new HashMap();
 83  
         extentTable = new HashMap();
 84  
         superClassMultipleJoinedTablesMap = new HashMap();
 85  
     }
 86  
 
 87  
     public static String getVersion()
 88  
     {
 89  
         return VERSION;
 90  
     }
 91  
 
 92  
     /**
 93  
      * Add a pair of extent/classdescriptor to the extentTable to gain speed
 94  
      * while retrieval of extents.
 95  
      * @param classname the name of the extent itself
 96  
      * @param cld the class descriptor, where it belongs to
 97  
      */
 98  
     void addExtent(String classname, ClassDescriptor cld)
 99  
     {
 100  
         synchronized (extentTable)
 101  
         {
 102  
             extentTable.put(classname, cld);
 103  
         }
 104  
     }
 105  
 
 106  
     /**
 107  
      * Remove a pair of extent/classdescriptor from the extentTable.
 108  
      * @param classname the name of the extent itself
 109  
      */
 110  
     void removeExtent(String classname)
 111  
     {
 112  
         synchronized (extentTable)
 113  
         {
 114  
             // returns the super class for given extent class name
 115  
             ClassDescriptor cld = (ClassDescriptor) extentTable.remove(classname);
 116  
             if(cld != null && m_topLevelClassTable != null)
 117  
             {
 118  
                 Class extClass = null;
 119  
                 try
 120  
                 {
 121  
                     extClass = ClassHelper.getClass(classname);
 122  
                 }
 123  
                 catch (ClassNotFoundException e)
 124  
                 {
 125  
                     // Should not happen
 126  
                     throw new MetadataException("Can't instantiate class object for needed extent remove", e);
 127  
                 }
 128  
                 // remove extent from super class descriptor
 129  
                 cld.removeExtentClass(classname);
 130  
                 m_topLevelClassTable.remove(extClass);
 131  
                 // clear map with first concrete classes, because the removed
 132  
                 // extent could be such a first found concrete class
 133  
                 m_firstConcreteClassMap = null;
 134  
             }
 135  
         }
 136  
     }
 137  
 
 138  
     /**
 139  
      * Returns the top level (extent) class to which the given class belongs.
 140  
      * This may be a (abstract) base-class, an interface or the given class
 141  
      * itself if given class is not defined as an extent in other class
 142  
      * descriptors.
 143  
      *
 144  
      * @throws ClassNotPersistenceCapableException if clazz is not persistence capable,
 145  
      * i.e. if clazz is not defined in the DescriptorRepository.
 146  
      */
 147  
     public Class getTopLevelClass(Class clazz) throws ClassNotPersistenceCapableException
 148  
     {
 149  
         if(m_topLevelClassTable == null)
 150  
         {
 151  
             m_topLevelClassTable = new HashMap();
 152  
         }
 153  
         // try to find an extent that contains clazz
 154  
         Class retval = (Class) m_topLevelClassTable.get(clazz);
 155  
         if (retval == null)
 156  
         {
 157  
             synchronized (extentTable)
 158  
             {
 159  
                 ClassDescriptor cld = (ClassDescriptor) extentTable.get(clazz.getName());
 160  
                 if (cld == null)
 161  
                 {
 162  
                     // walk the super-references
 163  
                     cld = getDescriptorFor(clazz).getSuperClassDescriptor();
 164  
                 }
 165  
                 
 166  
                 if (cld != null)
 167  
                 {
 168  
                     // fix by Mark Rowell
 169  
                     // Changed to call getExtentClass recursively
 170  
                     retval = getTopLevelClass(cld.getClassOfObject());
 171  
                     // if such an extent could not be found just return clazz itself.
 172  
                     if (retval == null)
 173  
                     {
 174  
                         retval = clazz;
 175  
                     }
 176  
                 }
 177  
                 else
 178  
                 {
 179  
                     // check if class is persistence capable
 180  
                     // +
 181  
                     // Adam Jenkins: use the class that is associated
 182  
                     // with the descriptor instead of the actual class
 183  
                     ClassDescriptor temp = getDescriptorFor(clazz);
 184  
                     retval = temp.getClassOfObject();
 185  
                 }
 186  
                 m_topLevelClassTable.put(clazz, retval);
 187  
             }
 188  
         }
 189  
         return retval;
 190  
     }
 191  
 
 192  
     /**
 193  
      * @return all field descriptors for a class that belongs to a set of classes mapped
 194  
      * to the same table, otherwise the select queries produced won't contain the necessary
 195  
      * information to materialize extents mapped to the same class.
 196  
      */
 197  
     public synchronized FieldDescriptor[] getFieldDescriptorsForMultiMappedTable(ClassDescriptor targetCld)
 198  
     {
 199  
         if (m_multiMappedTableMap == null)
 200  
         {
 201  
             m_multiMappedTableMap = new HashMap();
 202  
         }
 203  
 
 204  
         FieldDescriptor[] retval = (FieldDescriptor[]) m_multiMappedTableMap.get(targetCld.getClassNameOfObject());
 205  
         if (retval == null)
 206  
         {
 207  
             retval = getAllMappedColumns(getClassesMappedToSameTable(targetCld));
 208  
             m_multiMappedTableMap.put(targetCld.getClassNameOfObject(), retval);
 209  
         }
 210  
         return retval;
 211  
     }
 212  
 
 213  
     private FieldDescriptor[] getAllMappedColumns(List classDescriptors)
 214  
     {
 215  
         /* mkalen: Use an ordered implementation not to loose individual field ordering.
 216  
             This is especially important for eg Oracle9i platform and LONGVARBINARY columns,
 217  
             see http://download-west.oracle.com/docs/cd/B10501_01/java.920/a96654/basic.htm#1021777
 218  
             "If you do not use the SELECT-list order to access data,
 219  
              then you can lose the stream data."
 220  
         */
 221  
         List allFieldDescriptors = new Vector();
 222  
 
 223  
         Set visitedColumns = new HashSet();
 224  
         Iterator it = classDescriptors.iterator();
 225  
         ClassDescriptor temp = null;
 226  
         FieldDescriptor[] fields;
 227  
         while (it.hasNext())
 228  
         {
 229  
             temp = (ClassDescriptor) it.next();
 230  
             fields = temp.getFieldDescriptions();
 231  
             if (fields != null)
 232  
             {
 233  
                 for (int i = 0; i < fields.length; i++)
 234  
                 {
 235  
                     /*
 236  
                     MBAIRD
 237  
                     hashmap will only allow one entry per unique key,
 238  
                     so no need to check contains(fields[i].getColumnName()).
 239  
                     arminw:
 240  
                     use contains to avoid overriding of target class fields by the same
 241  
                     field-descriptor of other classes mapped to the same DB table, because
 242  
                     the other class can use e.g. different FieldConversion.
 243  
                     In #getClassesMappedToSameTable(...) we make sure that target
 244  
                     class has first position in list.
 245  
                      */
 246  
                     final String columnName = fields[i].getColumnName();
 247  
                     if (!visitedColumns.contains(columnName))
 248  
                     {
 249  
                         visitedColumns.add(columnName);
 250  
                         allFieldDescriptors.add(fields[i]);
 251  
                     }
 252  
                 }
 253  
             }
 254  
         }
 255  
         FieldDescriptor[] retval = new FieldDescriptor[allFieldDescriptors.size()];
 256  
         allFieldDescriptors.toArray(retval);
 257  
         return retval;
 258  
     }
 259  
 
 260  
     private List getClassesMappedToSameTable(ClassDescriptor targetCld)
 261  
     {
 262  
         /*
 263  
         try to find an extent that contains clazz
 264  
         clone map to avoid synchronization problems, because another thread
 265  
         can do a put(..) operation on descriptor table
 266  
         */
 267  
         Iterator iter = ((HashMap)descriptorTable.clone()).values().iterator();
 268  
         List retval = new ArrayList();
 269  
         // make sure that target class is at first position
 270  
         retval.add(targetCld);
 271  
         while (iter.hasNext())
 272  
         {
 273  
             ClassDescriptor cld = (ClassDescriptor) iter.next();
 274  
             if (cld.getFullTableName() != null)
 275  
             {
 276  
                 if (cld.getFullTableName().equals(targetCld.getFullTableName())
 277  
                         && !targetCld.getClassOfObject().equals(cld.getClassOfObject()))
 278  
                 {
 279  
                     retval.add(cld);
 280  
                 }
 281  
             }
 282  
         }
 283  
         return retval;
 284  
     }
 285  
 
 286  
     public Map getDescriptorTable()
 287  
     {
 288  
         return descriptorTable;
 289  
     }
 290  
 
 291  
     /**
 292  
      * Return the first found concrete class {@link ClassDescriptor}.
 293  
      * This means a class which is not an interface or an abstract class.
 294  
      * If given class descriptor is a concrete class, given class descriptor
 295  
      * was returned. If no concrete class can be found <code>null</code> will be
 296  
      * returned.
 297  
      */
 298  
     public ClassDescriptor findFirstConcreteClass(ClassDescriptor cld)
 299  
     {
 300  
         if(m_firstConcreteClassMap == null)
 301  
         {
 302  
             m_firstConcreteClassMap = new HashMap();
 303  
         }
 304  
         ClassDescriptor result = (ClassDescriptor) m_firstConcreteClassMap.get(cld.getClassNameOfObject());
 305  
         if (result == null)
 306  
         {
 307  
             if(cld.isInterface() || cld.isAbstract())
 308  
             {
 309  
                 if(cld.isExtent())
 310  
                 {
 311  
                     List extents = cld.getExtentClasses();
 312  
                     for (int i = 0; i < extents.size(); i++)
 313  
                     {
 314  
                         Class ext = (Class) extents.get(i);
 315  
                         result = findFirstConcreteClass(getDescriptorFor(ext));
 316  
                         if(result != null) break;
 317  
                     }
 318  
                 }
 319  
                 else
 320  
                 {
 321  
                     LoggerFactory.getDefaultLogger().error("["+this.getClass().getName()+"] Found interface/abstract class" +
 322  
                             " in metadata declarations without concrete class: "+cld.getClassNameOfObject());
 323  
                 }
 324  
                 m_firstConcreteClassMap.put(cld.getClassNameOfObject(), result);
 325  
             }
 326  
             else
 327  
             {
 328  
                 result = cld;
 329  
             }
 330  
         }
 331  
         return result;
 332  
     }
 333  
 
 334  
     /**
 335  
      *
 336  
      * Utility method to discover all concrete subclasses of a given super class. <br>
 337  
      * This method was introduced in order to get Extent Aware Iterators.
 338  
      *
 339  
      * @return a Collection of ClassDescriptor objects
 340  
      */
 341  
     public Collection getAllConcreteSubclassDescriptors(ClassDescriptor aCld)
 342  
     {
 343  
         if(m_allConcreteSubClass == null)
 344  
         {
 345  
             m_allConcreteSubClass = new HashMap();
 346  
         }
 347  
         Collection concreteSubclassClds = (Collection) m_allConcreteSubClass.get(aCld.getClassOfObject());
 348  
 
 349  
         if (concreteSubclassClds == null)
 350  
         {
 351  
             // BRJ: As long as we do not have an ordered Set
 352  
             // duplicates have to be prevented manually.
 353  
             // a HashSet should not be used because the order is unpredictable
 354  
             concreteSubclassClds = new ArrayList();
 355  
             Iterator iter = aCld.getExtentClasses().iterator();
 356  
 
 357  
             while (iter.hasNext())
 358  
             {
 359  
                 Class extentClass = (Class) iter.next();
 360  
                 ClassDescriptor extCld = getDescriptorFor(extentClass);
 361  
                 if (aCld.equals(extCld))
 362  
                 {
 363  
                     // prevent infinite recursion caused by cyclic references
 364  
                     continue;
 365  
                 }
 366  
                 if (!extCld.isInterface() && !extCld.isAbstract())
 367  
                 {
 368  
                     if (!concreteSubclassClds.contains(extCld))
 369  
                     {
 370  
                         concreteSubclassClds.add(extCld);
 371  
                     }
 372  
                 }
 373  
 
 374  
                 // recurse
 375  
                 Iterator subIter = getAllConcreteSubclassDescriptors(extCld).iterator();
 376  
                 while (subIter.hasNext())
 377  
                 {
 378  
                     ClassDescriptor subCld = (ClassDescriptor)subIter.next();
 379  
                     if (!concreteSubclassClds.contains(subCld))
 380  
                     {
 381  
                         concreteSubclassClds.add(subCld);
 382  
                     }
 383  
                 }
 384  
             }
 385  
             m_allConcreteSubClass.put(aCld.getClassOfObject(), concreteSubclassClds);
 386  
         }
 387  
 
 388  
         return concreteSubclassClds;
 389  
     }
 390  
 
 391  
 
 392  
     /**
 393  
      * Checks if repository contains given class.
 394  
      */
 395  
     public boolean hasDescriptorFor(Class c)
 396  
     {
 397  
         return descriptorTable.containsKey(c.getName());
 398  
     }
 399  
 
 400  
     /**
 401  
      * lookup a ClassDescriptor in the internal Hashtable
 402  
      * @param strClassName a fully qualified class name as it is returned by Class.getName().
 403  
      */
 404  
     public ClassDescriptor getDescriptorFor(String strClassName) throws ClassNotPersistenceCapableException
 405  
     {
 406  
         ClassDescriptor result = discoverDescriptor(strClassName);
 407  
         if (result == null)
 408  
         {
 409  
             throw new ClassNotPersistenceCapableException(strClassName + " not found in OJB Repository");
 410  
         }
 411  
         else
 412  
         {
 413  
             return result;
 414  
         }
 415  
     }
 416  
 
 417  
     /**
 418  
      * lookup a ClassDescriptor in the internal Hashtable
 419  
      */
 420  
     public ClassDescriptor getDescriptorFor(Class c) throws ClassNotPersistenceCapableException
 421  
     {
 422  
         return this.getDescriptorFor(c.getName());
 423  
     }
 424  
 
 425  
     /**
 426  
      * Convenience for {@link #put(Class c, ClassDescriptor cld)}
 427  
      */
 428  
     public void setClassDescriptor(ClassDescriptor cld)
 429  
     {
 430  
         this.put(cld.getClassNameOfObject(), cld);
 431  
     }
 432  
 
 433  
     /**
 434  
      * Add a ClassDescriptor to the internal Hashtable<br>
 435  
      * Set the Repository for ClassDescriptor
 436  
      */
 437  
     public void put(Class c, ClassDescriptor cld)
 438  
     {
 439  
         this.put(c.getName(), cld);
 440  
     }
 441  
 
 442  
     /**
 443  
      * Add a ClassDescriptor to the internal Hashtable<br>
 444  
      * Set the Repository for ClassDescriptor
 445  
      */
 446  
     public void put(String classname, ClassDescriptor cld)
 447  
     {
 448  
         cld.setRepository(this); // BRJ
 449  
         synchronized (descriptorTable)
 450  
         {
 451  
             descriptorTable.put(classname, cld);
 452  
             List extentClasses = cld.getExtentClasses();
 453  
             for (int i = 0; i < extentClasses.size(); ++i)
 454  
             {
 455  
                 addExtent(((Class) extentClasses.get(i)).getName(), cld);
 456  
             }
 457  
             changeDescriptorEvent();
 458  
         }
 459  
     }
 460  
 
 461  
     public void remove(String className)
 462  
     {
 463  
         synchronized (descriptorTable)
 464  
         {
 465  
             ClassDescriptor cld = (ClassDescriptor) descriptorTable.remove(className);
 466  
             if(cld != null)
 467  
             {
 468  
                 // class itself could no longer be a extent
 469  
                 Iterator it = descriptorTable.values().iterator();
 470  
                 while (it.hasNext())
 471  
                 {
 472  
                     ((ClassDescriptor) it.next()).removeExtentClass(className);
 473  
                 }
 474  
                 removeExtent(className);
 475  
                 List extentClasses = cld.getExtentClasses();
 476  
                 for (int i = 0; i < extentClasses.size(); ++i)
 477  
                 {
 478  
                     removeExtent(((Class) extentClasses.get(i)).getName());
 479  
                 }
 480  
                 changeDescriptorEvent();
 481  
                 // deregister classes using mapping of classes to multiple joined tables
 482  
                 // the registration is done by the class-descriptor itself
 483  
                 deregisterSuperClassMultipleJoinedTables(cld);
 484  
             }
 485  
         }
 486  
     }
 487  
 
 488  
     public void remove(Class clazz)
 489  
     {
 490  
         remove(clazz.getName());
 491  
     }
 492  
 
 493  
     private synchronized void changeDescriptorEvent()
 494  
     {
 495  
         m_multiMappedTableMap = null;
 496  
         m_topLevelClassTable = null;
 497  
         m_firstConcreteClassMap = null;
 498  
         m_allConcreteSubClass = null;
 499  
     }
 500  
 
 501  
     /**
 502  
      * Returns an iterator over all managed {@link ClassDescriptor}.
 503  
      */
 504  
     public Iterator iterator()
 505  
     {
 506  
         /*
 507  
         clone map to avoid synchronization problems
 508  
         */
 509  
         return ((HashMap)descriptorTable.clone()).values().iterator();
 510  
     }
 511  
 
 512  
     /**
 513  
      * Returns the defaultIsolationLevel.
 514  
      * @return int
 515  
      */
 516  
     public int getDefaultIsolationLevel()
 517  
     {
 518  
         return defaultIsolationLevel;
 519  
     }
 520  
 
 521  
     /**
 522  
      * Sets the defaultIsolationLevel.
 523  
      * @param defaultIsolationLevel The defaultIsolationLevel to set
 524  
      */
 525  
     public void setDefaultIsolationLevel(int defaultIsolationLevel)
 526  
     {
 527  
         this.defaultIsolationLevel = defaultIsolationLevel;
 528  
     }
 529  
 
 530  
     /**
 531  
      * returns a string representation
 532  
      */
 533  
     public String toString()
 534  
     {
 535  
         /**
 536  
          * Kuali Foundation modification -- 6/19/2009
 537  
          */
 538  
             synchronized (descriptorTable) {                        
 539  
         /**
 540  
          * End of Kuali Foundation modification
 541  
          */
 542  
                 Iterator it = descriptorTable.entrySet().iterator();
 543  
                 ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
 544  
                 String className = "class name: ";
 545  
                 String tableName = "> table name: ";
 546  
                 while (it.hasNext())
 547  
                 {
 548  
                     Map.Entry me = (Map.Entry) it.next();
 549  
                     ClassDescriptor descriptor = (ClassDescriptor) me.getValue();
 550  
                     buf.append(className + me.getKey() + " =", tableName + descriptor.getFullTableName());
 551  
                 }
 552  
                 return buf.toString();
 553  
         /**
 554  
          * Kuali Foundation modification -- 6/19/2009
 555  
          */
 556  
                 }
 557  
         /**
 558  
          * End of Kuali Foundation modification
 559  
          */
 560  
     }
 561  
 
 562  
     /*
 563  
      * @see XmlCapable#toXML()
 564  
      */
 565  
     public String toXML()
 566  
     {
 567  
         String eol = SystemUtils.LINE_SEPARATOR;
 568  
         StringBuffer buf = new StringBuffer();
 569  
 
 570  
         // write all ClassDescriptors
 571  
         Iterator i = this.iterator();
 572  
         while (i.hasNext())
 573  
         {
 574  
             buf.append(((XmlCapable) i.next()).toXML() + eol);
 575  
         }
 576  
         return buf.toString();
 577  
     }
 578  
 
 579  
     /**
 580  
      * returns IsolationLevel literal as matching
 581  
      * to the corresponding id
 582  
      * @return the IsolationLevel literal
 583  
      */
 584  
     protected String getIsolationLevelAsString()
 585  
     {
 586  
         if (defaultIsolationLevel == IL_READ_UNCOMMITTED)
 587  
         {
 588  
             return LITERAL_IL_READ_UNCOMMITTED;
 589  
         }
 590  
         else if (defaultIsolationLevel == IL_READ_COMMITTED)
 591  
         {
 592  
             return LITERAL_IL_READ_COMMITTED;
 593  
         }
 594  
         else if (defaultIsolationLevel == IL_REPEATABLE_READ)
 595  
         {
 596  
             return LITERAL_IL_REPEATABLE_READ;
 597  
         }
 598  
         else if (defaultIsolationLevel == IL_SERIALIZABLE)
 599  
         {
 600  
             return LITERAL_IL_SERIALIZABLE;
 601  
         }
 602  
         else if (defaultIsolationLevel == IL_OPTIMISTIC)
 603  
         {
 604  
             return LITERAL_IL_OPTIMISTIC;
 605  
         }
 606  
         return LITERAL_IL_READ_UNCOMMITTED;
 607  
     }
 608  
 
 609  
     /**
 610  
      * Starts by looking to see if the <code>className</code> is
 611  
      * already mapped specifically to the descritpor repository.
 612  
      * If the <code>className</code> is not specifically mapped we
 613  
      * look at the <code>className</code>'s parent class for a mapping.
 614  
      * We do this until the parent class is of the type
 615  
      * <code>java.lang.Object</code>.  If no mapping was found,
 616  
      * <code>null</code> is returned.  Mappings successfuly discovered
 617  
      * through inheritence are added to the internal table of
 618  
      * class descriptors to improve performance on subsequent requests
 619  
      * for those classes.
 620  
      *
 621  
      * <br/>
 622  
      * author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
 623  
      * @param className name of class whose descriptor we need to find.
 624  
      * @return ClassDescriptor for <code>className</code> or <code>null</code>
 625  
      * if no ClassDescriptor could be located.
 626  
      */
 627  
     protected ClassDescriptor discoverDescriptor(String className)
 628  
     {
 629  
         ClassDescriptor result = (ClassDescriptor) descriptorTable.get(className);
 630  
         if (result == null)
 631  
         {
 632  
             Class clazz;
 633  
             try
 634  
             {
 635  
                 clazz = ClassHelper.getClass(className, true);
 636  
             }
 637  
             catch (ClassNotFoundException e)
 638  
             {
 639  
                 throw new OJBRuntimeException("Class, " + className + ", could not be found.", e);
 640  
             }
 641  
             result = discoverDescriptor(clazz);
 642  
          }
 643  
         return result;
 644  
     }
 645  
 
 646  
     /**
 647  
      * Internal method for recursivly searching for a class descriptor that avoids
 648  
      * class loading when we already have a class object.
 649  
      *
 650  
      * @param clazz The class whose descriptor we need to find
 651  
      * @return ClassDescriptor for <code>clazz</code> or <code>null</code>
 652  
      *         if no ClassDescriptor could be located.
 653  
      */
 654  
     private ClassDescriptor discoverDescriptor(Class clazz)
 655  
     {
 656  
         ClassDescriptor result = (ClassDescriptor) descriptorTable.get(clazz.getName());
 657  
 
 658  
         if (result == null)
 659  
         {
 660  
             Class superClass = clazz.getSuperclass();
 661  
             // only recurse if the superClass is not java.lang.Object
 662  
             if (superClass != null)
 663  
             {
 664  
                 result = discoverDescriptor(superClass);
 665  
             }
 666  
             if (result == null)
 667  
             {
 668  
                 // we're also checking the interfaces as there could be normal
 669  
                 // mappings for them in the repository (using factory-class,
 670  
                 // factory-method, and the property field accessor)
 671  
                 Class[] interfaces = clazz.getInterfaces();
 672  
 
 673  
                 if ((interfaces != null) && (interfaces.length > 0))
 674  
                 {
 675  
                     for (int idx = 0; (idx < interfaces.length) && (result == null); idx++)
 676  
                     {
 677  
                         result = discoverDescriptor(interfaces[idx]);
 678  
                     }
 679  
                 }
 680  
             }
 681  
 
 682  
             if (result != null)
 683  
             {
 684  
                 /**
 685  
                  * Kuali Foundation modification -- 6/19/2009
 686  
                  */
 687  
                     synchronized (descriptorTable) {
 688  
                 /**
 689  
                  * End of Kuali Foundation modification
 690  
                  */
 691  
                             descriptorTable.put(clazz.getName(), result);
 692  
                 /**
 693  
                  * Kuali Foundation modification -- 6/19/2009
 694  
                  */
 695  
                     }
 696  
                 /**
 697  
                  * End of Kuali Foundation modification
 698  
                  */
 699  
             }
 700  
         }
 701  
         return result;
 702  
     }
 703  
 
 704  
     /**
 705  
      * Internal used! Register sub-classes of specified class when mapping class to
 706  
      * multiple joined tables is used. Normally this method is called by the {@link ClassDescriptor}
 707  
      * itself.
 708  
      *
 709  
      * @param cld The {@link ClassDescriptor} of the class to register.
 710  
      */
 711  
     protected void registerSuperClassMultipleJoinedTables(ClassDescriptor cld)
 712  
     {
 713  
         /*
 714  
         arminw: Sadly, we can't map to sub class-descriptor, because it's not guaranteed
 715  
         that they exist when this method is called. Thus we map the class instance instead
 716  
         of the class-descriptor.
 717  
         */
 718  
         if(cld.getBaseClass() != null)
 719  
         {
 720  
             try
 721  
             {
 722  
                 Class superClass = ClassHelper.getClass(cld.getBaseClass());
 723  
                 Class currentClass = cld.getClassOfObject();
 724  
                 synchronized(descriptorTable)
 725  
                 {
 726  
                     List subClasses = (List) superClassMultipleJoinedTablesMap.get(superClass);
 727  
                     if(subClasses == null)
 728  
                     {
 729  
                         subClasses = new ArrayList();
 730  
                         superClassMultipleJoinedTablesMap.put(superClass,  subClasses);
 731  
                     }
 732  
                     if(!subClasses.contains(currentClass))
 733  
                     {
 734  
                         if(log.isDebugEnabled())
 735  
                         {
 736  
                             log.debug("(MultipleJoinedTables): Register sub-class '" + currentClass
 737  
                                     + "' for class '" + superClass);
 738  
                         }
 739  
                         subClasses.add(currentClass);
 740  
                     }
 741  
                 }
 742  
             }
 743  
             catch(Exception e)
 744  
             {
 745  
                 throw new MetadataException("Can't register super class '" + cld.getBaseClass()
 746  
                         + "' for class-descriptor: " + cld, e);
 747  
             }
 748  
         }
 749  
     }
 750  
 
 751  
     /**
 752  
      * Internal used! Deregister sub-classes of specified class when mapping to multiple joined tables
 753  
      * is used. Normally this method is called when {@link #remove(Class)} a class.
 754  
      *
 755  
      * @param cld The {@link ClassDescriptor} of the class to register.
 756  
      */
 757  
     protected void deregisterSuperClassMultipleJoinedTables(ClassDescriptor cld)
 758  
     {
 759  
         try
 760  
         {
 761  
             Class currentClass = cld.getClassOfObject();
 762  
             synchronized(descriptorTable)
 763  
             {
 764  
                 // first remove registered sub-classes for current class
 765  
                 List subClasses = (List) superClassMultipleJoinedTablesMap.remove(currentClass);
 766  
                 if(subClasses != null && log.isDebugEnabled())
 767  
                 {
 768  
                     log.debug("(MultipleJoinedTables): Deregister class " + currentClass
 769  
                             + " with sub classes " + subClasses);
 770  
                 }
 771  
                 if(cld.getBaseClass() != null)
 772  
                 {
 773  
                     // then remove sub-class entry of current class for super-class
 774  
                     Class superClass = ClassHelper.getClass(cld.getBaseClass());
 775  
                     subClasses = (List) superClassMultipleJoinedTablesMap.get(superClass);
 776  
                     if(subClasses != null)
 777  
                     {
 778  
                         boolean removed = subClasses.remove(currentClass);
 779  
                         if(removed && log.isDebugEnabled())
 780  
                         {
 781  
                             log.debug("(MultipleJoinedTables): Remove sub-class entry '" + currentClass
 782  
                             + "' in mapping for class '" + superClass + "'");
 783  
                         }
 784  
                     }
 785  
                 }
 786  
             }
 787  
         }
 788  
         catch(Exception e)
 789  
         {
 790  
             throw new MetadataException("Can't deregister super class '" + cld.getBaseClass()
 791  
                     + "' for class-descriptor: " + cld, e);
 792  
         }
 793  
     }
 794  
 
 795  
     /**
 796  
      * Return <em>sub-classes</em> of the specified class using the
 797  
      * <em>"super"-Reference</em> concept.
 798  
      * @param cld The {@link ClassDescriptor} of the class to search for sub-classes.
 799  
      * @param wholeTree If set <em>true</em>, the whole sub-class tree of the specified
 800  
      * class will be returned. If <em>false</em> only the direct sub-classes of the specified class
 801  
      * will be returned.
 802  
      * @return An array of <em>sub-classes</em> for the specified class.
 803  
      */
 804  
     public Class[] getSubClassesMultipleJoinedTables(ClassDescriptor cld, boolean wholeTree)
 805  
     {
 806  
         ArrayList result = new ArrayList();
 807  
         createResultSubClassesMultipleJoinedTables(result, cld, wholeTree);
 808  
         return (Class[]) result.toArray(new Class[result.size()]);
 809  
     }
 810  
 
 811  
     /**
 812  
      * Add all sub-classes using multiple joined tables feature for specified class.
 813  
      * @param result The list to add results.
 814  
      * @param cld The {@link ClassDescriptor} of the class to search for sub-classes.
 815  
      * @param wholeTree If set <em>true</em>, the whole sub-class tree of the specified
 816  
      * class will be returned. If <em>false</em> only the direct sub-classes of the specified class
 817  
      * will be returned.
 818  
      */
 819  
     private void createResultSubClassesMultipleJoinedTables(List result, ClassDescriptor cld, boolean wholeTree)
 820  
     {
 821  
         List tmp = (List) superClassMultipleJoinedTablesMap.get(cld.getClassOfObject());
 822  
         if(tmp != null)
 823  
         {
 824  
             result.addAll(tmp);
 825  
             if(wholeTree)
 826  
             {
 827  
                 for(int i = 0; i < tmp.size(); i++)
 828  
                 {
 829  
                     Class subClass = (Class) tmp.get(i);
 830  
                     ClassDescriptor subCld = getDescriptorFor(subClass);
 831  
                     createResultSubClassesMultipleJoinedTables(result, subCld, wholeTree);
 832  
                 }
 833  
             }
 834  
         }
 835  
     }
 836  
 
 837  
     protected void finalize() throws Throwable
 838  
     {
 839  
         log.info("# finalize DescriptorRepository instance #");
 840  
         super.finalize();
 841  
     }
 842  
 }