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 }