001    package org.apache.ojb.broker.accesslayer;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.lang.reflect.Constructor;
019    import java.sql.SQLException;
020    import java.util.Enumeration;
021    import java.util.NoSuchElementException;
022    
023    import org.apache.ojb.broker.Identity;
024    import org.apache.ojb.broker.PersistenceBroker;
025    import org.apache.ojb.broker.PersistenceBrokerException;
026    import org.apache.ojb.broker.PersistenceBrokerSQLException;
027    import org.apache.ojb.broker.metadata.ClassDescriptor;
028    import org.apache.ojb.broker.metadata.FieldDescriptor;
029    import org.apache.ojb.broker.query.Query;
030    import org.apache.ojb.broker.util.ConstructorHelper;
031    import org.apache.ojb.broker.util.logging.LoggerFactory;
032    
033    /**
034     * this class can be used to create enumerations of PrimaryKey objects.
035     * This is interesting for EJB finder methods
036     * in BMP entity beans which must return such enumerations.
037     * @author Thomas Mahler
038     * @version $Id: PkEnumeration.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
039     */
040    public class PkEnumeration implements Enumeration
041    {
042            static final long serialVersionUID = -834955711995869884L;
043        protected boolean hasCalledCheck = false;
044        protected boolean hasNext = false;
045    
046        protected PersistenceBroker broker;
047    
048        /**
049         * The underlying jdbc resultset produced by select statement
050         */
051        protected ResultSetAndStatement resultSetAndStatment;
052    
053        /**
054         * descriptor for the class of which items are to be found
055         */
056        protected ClassDescriptor classDescriptor;
057    
058        /**
059         * the Constructor that is needed to build the PrimaryKey Objects
060         */
061        protected Constructor constructor;
062    
063        /**
064         * PkEnumeration constructor.
065         * @param query the SELECT statement gerating the underlying resultset
066         * @param cld classDescriptor of the target entity class (say Article)
067         * @param primaryKeyClass the entity classes PrimaryKey class (say ArticleKey).
068         * this key-class MUST have a constructor with one argument of type org.apache.ojb.broker.Identity !
069         */
070        public PkEnumeration(Query query, ClassDescriptor cld, Class primaryKeyClass, PersistenceBroker broker)
071        {
072            this.resultSetAndStatment = broker.serviceJdbcAccess().executeQuery(query, cld);
073            this.classDescriptor = cld;
074            this.broker = broker;
075            // get a contructor object that can be used to build instances of class primaryKeyClass
076            try
077            {
078                Class[] argArray = {Identity.class};
079                this.constructor = primaryKeyClass.getConstructor(argArray);
080            }
081            catch (NoSuchMethodException e)
082            {
083                LoggerFactory.getDefaultLogger().error(primaryKeyClass.getName()
084                        + " must implement a Constructor with one argument of type org.apache.ojb.broker.Identity");
085                throw new PersistenceBrokerException(e);
086            }
087            catch (SecurityException e)
088            {
089                LoggerFactory.getDefaultLogger().error(e);
090                throw new PersistenceBrokerException(e);
091            }
092        }
093    
094        /**
095         * returns an Identity object representing the current resultset row
096         */
097        private Identity getIdentityFromResultSet()
098        {
099    
100            try
101            {
102                // 1. get an empty instance of the target class
103                Constructor con = classDescriptor.getZeroArgumentConstructor();
104                Object obj = ConstructorHelper.instantiate(con);
105    
106                // 2. fill only primary key values from Resultset
107                Object colValue;
108                FieldDescriptor fld;
109                FieldDescriptor[] pkfields = classDescriptor.getPkFields();
110                for (int i = 0; i < pkfields.length; i++)
111                {
112                    fld = pkfields[i];
113                    colValue = fld.getJdbcType().getObjectFromColumn(resultSetAndStatment.m_rs, fld.getColumnName());
114                    fld.getPersistentField().set(obj, colValue);
115                }
116                // 3. return the representing identity object
117                return broker.serviceIdentity().buildIdentity(classDescriptor, obj);
118            }
119            catch (SQLException e)
120            {
121                throw new PersistenceBrokerSQLException("Error reading object from column", e);
122            }
123            catch (Exception e)
124            {
125                throw new PersistenceBrokerException("Error reading Identity from result set", e);
126            }
127        }
128    
129        /**
130         * Tests if this enumeration contains more elements.
131         * @return  <code>true</code> if and only if this enumeration object
132         * contains at least one more element to provide;
133         * <code>false</code> otherwise.
134         */
135        public boolean hasMoreElements()
136        {
137            try
138            {
139                if (!hasCalledCheck)
140                {
141                    hasCalledCheck = true;
142                    hasNext = resultSetAndStatment.m_rs.next();
143                }
144            }
145            catch (SQLException e)
146            {
147                LoggerFactory.getDefaultLogger().error(e);
148                //releaseDbResources();
149                hasNext = false;
150            }
151            finally
152            {
153                if(!hasNext)
154                {
155                    releaseDbResources();
156                }
157            }
158            return hasNext;
159        }
160    
161        private void releaseDbResources()
162        {
163            resultSetAndStatment.close();
164            resultSetAndStatment = null;
165        }
166    
167        /**
168         * Returns the next element of this enumeration if this enumeration
169         * object has at least one more element to provide.
170         * @return     the next element of this enumeration.
171         * @exception  NoSuchElementException  if no more elements exist.
172         */
173        public Object nextElement()
174        {
175            try
176            {
177                if (!hasCalledCheck)
178                {    
179                    hasMoreElements();
180                }    
181                hasCalledCheck = false;
182                if (hasNext)
183                {
184                    Identity oid = getIdentityFromResultSet();
185                    Identity[] args = {oid};
186                    return this.constructor.newInstance(args);
187                }
188                else
189                    throw new NoSuchElementException();
190            }
191            catch (Exception ex)
192            {
193                LoggerFactory.getDefaultLogger().error(ex);
194                throw new NoSuchElementException();
195            }
196        }
197    
198            /**
199             * protection just in case someone leaks.
200             */
201            protected void finalize()
202            {
203                    if(resultSetAndStatment != null)
204            {
205                LoggerFactory.getDefaultLogger().error("["+PkEnumeration.class.getName()+"] Found unclosed resources while finalize");
206                releaseDbResources();
207            }
208            }
209    }