View Javadoc

1   /* Version 1.0 based on Apache Software License 1.1
2    *
3    * Copyright (c) 2003 Piotr Maj and DBMonster developers. All rights
4    * reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are
8    * met:
9    *
10   * 1. Redistributions of source code must retain the above copyright
11   *    notice, this list of conditions and the following disclaimer.
12   *
13   * 2. Redistributions in binary form must reproduce the above copyright
14   *    notice, this list of conditions and the following disclaimer in the
15   *    documentation and/or other materials provided with the distribution.
16   *
17   * 3. The end-user documentation included with the redistribution, if any,
18   *    must include the following acknowledgment:
19   *
20   *    "This product includes software developed by DBMonster developers
21   *    (http://dbmonster.kernelpanic.pl/)."
22   *
23   *  Alternately, this acknowledgment may appear in the software itself,
24   *  if and wherever such third-party acknowledgments normally appear.
25   *
26   * 4. The name "DBMonster" must not be used to endorse or promote products
27   *    derived from this software without prior written permission. For
28   *    written permission, please contact piotr.maj@kernelpanic.pl.
29   *
30   * 5. Products derived from this software may not be called "DBMonster",
31   *    nor may "DBMonster" appear in their name, without prior written
32   *    permission of Piotr Maj.
33   *
34   * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
35   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
37   * IN NO EVENT SHALL THE DBMONSTER DEVELOPERS BE LIABLE FOR ANY DIRECT,
38   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
40   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
43   * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
44   * POSSIBILITY OF SUCH DAMAGE.
45   */
46  
47  package pl.kernelpanic.dbmonster.generator;
48  
49  import java.sql.ResultSet;
50  import java.util.ArrayList;
51  import java.util.Iterator;
52  import java.util.List;
53  import java.util.Random;
54  
55  import pl.kernelpanic.dbmonster.DBMonster;
56  import pl.kernelpanic.dbmonster.DBMonsterContext;
57  import pl.kernelpanic.dbmonster.connection.ConnectionProvider;
58  import pl.kernelpanic.dbmonster.connection.Transaction;
59  import pl.kernelpanic.dbmonster.schema.Column;
60  import pl.kernelpanic.dbmonster.schema.Key;
61  import pl.kernelpanic.dbmonster.schema.Schema;
62  import pl.kernelpanic.dbmonster.schema.SchemaException;
63  import pl.kernelpanic.dbmonster.schema.Table;
64  
65  import org.apache.commons.logging.Log;
66  
67  /***
68   * Foreign key generator.
69   *
70   * @author Piotr Maj <piotr.maj@kernelpanic.pl>
71   *
72   * @version $Id: ForeignKeyGenerator.html,v 1.1 2007/06/21 08:38:13 sbahloul Exp $
73   */
74  public class ForeignKeyGenerator extends BasicDataGenerator implements Initializable {
75  
76      /***
77       * The name of the column.
78       */
79      private String columnName = null;
80  
81      /***
82       * The name of the table.
83       */
84      private String tableName = null;
85  
86      /***
87       * Turns on the fast mode.
88       */
89      private boolean fastMode = false;
90  
91      /***
92       * A list of items stored in memmory in fast mode.
93       */
94      private List items = null;
95  
96      /***
97       * Context.
98       */
99      private DBMonsterContext context = null;
100 
101     /***
102      * Connection provider.
103      */
104     private ConnectionProvider connProv = null;
105 
106     /***
107      * Logger.
108      */
109     private Log log = null;
110 
111     /***
112      * Random number generator.
113      */
114     private Random random = null;
115 
116     /***
117      * An SQL statement used to get values;
118      */
119     private String sql = null;
120 
121     /***
122      * For internal use only.
123      */
124     private boolean dependencyGenerated = false;
125 
126     /***
127      * For internal use only.
128      */
129     private boolean initialized = false;
130 
131     /***
132      * Initializes the generator.
133      *
134      * @param ctx context
135      *
136      * @throws Exception if generator cannot be initialized
137      */
138     public final void initialize(final DBMonsterContext ctx) throws Exception {
139         if (columnName == null || tableName == null) {
140             throw new Exception("No columnName or tableName property set!");
141         }
142 
143         context = ctx;
144         connProv = (ConnectionProvider) context.getProperty(
145             DBMonster.CONNECTION_PROVIDER_KEY);
146         log = (Log) context.getProperty(DBMonster.LOGGER_KEY);
147         random = (Random) context.getProperty(DBMonster.RANDOM_KEY);
148 
149         StringBuffer buff = new StringBuffer();
150         buff.append("SELECT ");
151         buff.append(columnName);
152         buff.append(" FROM ");
153         buff.append(tableName);
154         sql = buff.toString();
155     }
156 
157     /***
158      * Generates a value using foreign key.
159      *
160      * @return value
161      *
162      * @throws Exception if generation fails.
163      */
164     public final Object generate() throws Exception {
165         Schema schema = column.getTable().getSchema();
166         Table table = schema.findTable(tableName);
167         if (!dependencyGenerated) {
168             if (log.isDebugEnabled()) {
169                 log.debug("Ensure that table <" + tableName
170                     + "> is already generated.");
171             }
172             if (table == null) {
173                 throw new SchemaException("No table <"
174                     + tableName + "> in this schema!");
175             }
176             if (!table.getName().equals(getColumn().getTable().getName())) {
177                 table.generate();
178             } else {
179                 fastMode = false;
180             }
181             if (log.isDebugEnabled()) {
182                 log.debug("Yup! The table <" + tableName
183                     + "> is already generated.");
184             }
185             dependencyGenerated = true;
186         }
187 
188         if (fastMode) {
189             if (!initialized) {
190                 items = new ArrayList();
191                 Transaction tx = null;
192                 try {
193                     tx = new Transaction(connProv);
194                     tx.begin();
195                     ResultSet rs = tx.executeQuery(sql);
196                     while (rs.next()) {
197                         items.add(rs.getObject(1));
198                     }
199                     tx.commit();
200                 } catch (Exception e) {
201                     tx.abort();
202                     if (log.isDebugEnabled()) {
203                         log.debug(e.getMessage());
204                     }
205                     throw e;
206                 } finally {
207                     tx.close();
208                 }
209                 initialized = true;
210             }
211 
212             if (items.size() == 0) {
213                 throw new Exception("No items in foreign key.");
214             }
215             return items.get(random.nextInt(items.size()));
216         }
217 
218         Transaction tx = null;
219         int rowsCount = 0;
220         try {
221             tx = new Transaction(connProv);
222             tx.begin();
223             ResultSet rs = tx.executeQuery("SELECT count(*) FROM " + tableName);
224             rs.next();
225             rowsCount = rs.getInt(1);
226             tx.commit();
227 
228             if (rowsCount == 0) {
229                 if (table.getName().equals(tableName)) {
230                     // check if we have to deal with self referenced table
231                     Column referencedColumn = null;
232                     // search the key
233                     Key key = table.getKey();
234                     if (key != null) {
235                         List columns = key.getGenerator().getColumns();
236                         for (int i = 0; i < columns.size(); i++) {
237                             referencedColumn = (Column) columns.get(i);
238                             if (referencedColumn.getName().equals(columnName)) {
239                                 break;
240                             }
241                             referencedColumn = null;
242                         }
243                     }
244                     if (referencedColumn == null) {
245                         // search the columns
246                         Iterator it = table.columnIterator();
247                         while (it.hasNext()) {
248                             Column column = (Column) it.next();
249                             if (column.getName().equals(columnName)) {
250                                 column.generate();
251                             }
252                         }
253                     }
254 
255                     return referencedColumn.getValue();
256                 }
257                 throw new Exception("There are no rows in table "
258                     + tableName);
259             }
260             rs = tx.executeQuery(sql);
261             int rowNum = random.nextInt(rowsCount);
262             int counter = 0;
263             while (counter <= rowNum) {
264                 rs.next();
265                 ++counter;
266             }
267             return rs.getObject(1);
268         } catch (Exception e) {
269             tx.abort();
270             if (log.isDebugEnabled()) {
271                 log.debug(e.getMessage());
272             }
273             throw e;
274         } finally {
275             tx.close();
276         }
277     }
278 
279     /***
280      * Returns column name.
281      *
282      * @return column name
283      */
284     public final String getColumnName() {
285         return columnName;
286     }
287 
288     /***
289      * Sets the column name.
290      *
291      * @param name column name
292      */
293     public final void setColumnName(final String name) {
294         columnName = name;
295     }
296 
297     /***
298      * Returns table name.
299      *
300      * @return table name
301      */
302     public final String getTableName() {
303         return tableName;
304     }
305 
306     /***
307      * Sets the table name.
308      *
309      * @param name table name
310      */
311     public final void setTableName(final String name) {
312         tableName = name;
313     }
314 
315     /***
316      * Returns fast mode parameter.
317      *
318      * @return fast mode
319      */
320     public final boolean getFastMode() {
321         return fastMode;
322     }
323 
324     /***
325      * Sets the fast mode parameter.
326      *
327      * @param mode fast mode parameter
328      */
329     public final void setFastMode(final boolean mode) {
330         fastMode = mode;
331     }
332 
333     /***
334      * @see pl.kernelpanic.dbmonster.generator.DataGenerator#reset()
335      */
336     public final void reset() {
337         initialized = false;
338         dependencyGenerated = false;
339     }
340 }