KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > dinamica > GenericTransaction


1 package dinamica;
2
3 import java.util.HashMap JavaDoc;
4 import javax.naming.*;
5 import javax.sql.DataSource JavaDoc;
6
7
8 /**
9  * Base class to program business transaction services (read/write).
10  * All transactions will subclass this class.
11  *
12  * <br>
13  * Creation date: 4/10/2003<br>
14  * Last Update: 4/10/2003<br>
15  * (c) 2003 Martin Cordova<br>
16  * This code is released under the LGPL license<br>
17  * @author Martin Cordova
18  */

19 public class GenericTransaction extends AbstractModule
20 {
21     
22     /** store recordsets published by this service */
23     private HashMap JavaDoc _publish = new HashMap JavaDoc();
24     
25     /**
26      * Publish recordset to be consumed by Output modules
27      * @param key Recordset ID
28      * @param data Recordset object
29      */

30     protected void publish(String JavaDoc key, Recordset data) throws Throwable JavaDoc
31     {
32         _publish.put(key, data);
33
34         /* get recordset simple metadata (recordcount, pagecount, etc) */
35         data.setID(key);
36         Recordset info = data.getRecordsetInfo();
37             
38         /* publish this new recordset */
39         String JavaDoc infoID = key + ".metadata";
40         _publish.put(infoID, info);
41                 
42     }
43     
44     /**
45      * Transaction service - this method must be redefined
46      * by descendants of this class, include a super.service(inputParams)
47      * as the first line of your service() method code to reuse base
48      * functionality (auto-creation of recordsets based on recordset elements defined in config.xml).
49      * In this method the business logic will be contained, and results will be represented
50      * as recordsets that will be consumed by Output objects. Recordsets are published using
51      * the method publish(id, rsObject). This class provides a method to retrieve
52      * a recordset using its ID and throws an error if the recordset is not present in the HashMap.<br>
53      * If inputParams is not null then it is published with the id "_request".
54      * @param inputParams Request parameters pre-validated and represented as a Recordset with one record.
55      * Recordset fields are set according to the data types defined in the validator.xml file.
56      * @return 0 if success - any other return values are user defined
57      * @throws Throwable
58      */

59     public int service(Recordset inputParams) throws Throwable JavaDoc
60     {
61         int rc = createRecordsets(inputParams);
62         
63         if (inputParams!=null)
64             _publish.put("_request", inputParams);
65         
66         return rc;
67         
68     }
69     
70     /**
71      * Create recordsets using config.xml parameters.
72      * For recordsets created using SQL templates, all values
73      * from the inputParams recordset will be auto-replaced into
74      * the template. This recordset is only created when using
75      * a validator (validator.xml definition to auto-validate request parameters).
76      * @throws Throwable in case of invalid config.xml parameters or JDBC exceptions.
77      */

78     protected int createRecordsets(Recordset inputParams) throws Throwable JavaDoc
79     {
80     
81         int rc = 0;
82     
83         /* get database object */
84         Db db = getDb();
85     
86         /* get recordsets config */
87         Recordset rs = _config.getRecordsets();
88         Recordset rs1 = null;
89         
90         /* for each defined recordset */
91         while (rs.next())
92         {
93             
94             /* get parameters */
95             String JavaDoc id = (String JavaDoc)rs.getValue("id");
96             String JavaDoc source = (String JavaDoc)rs.getValue("source");
97             String JavaDoc scope = (String JavaDoc)rs.getValue("scope");
98             String JavaDoc onempty = (String JavaDoc)rs.getValue("onempty");
99             String JavaDoc maxRows = (String JavaDoc)rs.getValue("maxrows");
100             int limit = 0;
101             if (maxRows!=null)
102                 limit = Integer.parseInt(maxRows);
103             String JavaDoc dataSrc = rs.getString("datasource");
104             String JavaDoc params = rs.getString("params");
105
106             /* create recordset using appropiate source */
107             if (source.equals("sql"))
108             {
109                 
110                 String JavaDoc sqlFile = getResource(id);
111                 TemplateEngine t = new TemplateEngine(_ctx, _req, sqlFile);
112                 if (params==null)
113                     sqlFile = t.getSql(inputParams);
114                 else {
115                     // PATCH 2005-04-05 support for alternative input parameters recordset for SQL templates
116
Recordset rsParams = getRecordset(params);
117                     if (rsParams.getRecordCount()>0)
118                         rsParams.first();
119                     else
120                         throw new Throwable JavaDoc("The recordset [" + params + "] used to replace SQL template values is empty.");
121                     sqlFile = t.getSql(rsParams);
122                 }
123                 
124                 //PATCH 2005-03-14 support datasource defined at recordset level
125
if (dataSrc==null)
126                 {
127                     if (limit>0)
128                         rs1 = db.get(sqlFile, limit);
129                     else
130                         rs1 = db.get(sqlFile);
131                 }
132                 else
133                 {
134                     rs1 = dbGet(dataSrc, sqlFile, limit);
135                 }
136                 
137                 if (onempty!=null)
138                 {
139                     if (rs1.getRecordCount()==0)
140                         rc = Integer.parseInt(onempty);
141                 }
142             }
143             else if (source.equals("session"))
144             {
145                 rs1 = (Recordset)getSession().getAttribute(id);
146                 
147                 //PATCH 2005-03-01 - enhance error message if session attribute is null
148
if (rs1==null)
149                     throw new Throwable JavaDoc("Recordset [" + id + "] not found in Session attribute, maybe the application was reloaded, destroying the session.");
150             }
151             else if (source.equals("request"))
152             {
153                 rs1 = (Recordset)_req.getAttribute(id);
154             }
155             else if (source.equals("textfile"))
156             {
157                 rs1 = this.getRsFromFlatFile(id);
158             }
159             else if (source.equals("class"))
160             {
161                 IRecordsetProvider rsProv = (IRecordsetProvider)getObject(id);
162                 rs1 = rsProv.getRecordset(inputParams);
163             }
164             else
165             {
166                 throw new Throwable JavaDoc("Invalid recordset source in config.xml (" + _config.path + "). Source attribute values can be sql, session, textfile or request only.");
167             }
168
169             /* publish this recordset */
170             _publish.put(id, rs1);
171             
172             /* get recordset simple metadata (recordcount, pagecount, etc) */
173             rs1.setID(id);
174             Recordset info = rs1.getRecordsetInfo();
175             
176             /* publish this new recordset */
177             String JavaDoc infoID = id + ".metadata";
178             _publish.put(infoID, info);
179             
180             /* persist recordset if necessary (in session or request object */
181             if (scope.equals("session"))
182             {
183                 getSession().setAttribute(id, rs1);
184             }
185             else if (scope.equals("request"))
186             {
187                 _req.setAttribute(id, rs1);
188             }
189             else if (!scope.equals("transaction"))
190             {
191                 throw new Throwable JavaDoc("Invalid recordset scope in config.xml (" + _config.path + "). Scope attribute values can be transaction, session or request only.");
192             }
193             
194         }
195         
196         return rc;
197     }
198     
199     /**
200      * Returns a recordset published by this transaction
201      * @param id ID or symbolic name which was used to publish the
202      * recordset - either by code or using the config.xml elements.
203      * @return Recordset
204      * @throws Throwable if ID oes not match any of the IDs of the published recordsets
205      */

206     public Recordset getRecordset(String JavaDoc id) throws Throwable JavaDoc
207     {
208         if (_publish.containsKey(id))
209         {
210             return (Recordset)_publish.get(id);
211         }
212         else
213         {
214             throw new Throwable JavaDoc("Invalid recordset ID: " + id);
215         }
216     }
217     
218     /**
219      * Generate SQL command. Encapsulates the use of the TemplateEngine
220      * class, to make it easier for developers writing Transaction Modules
221      * @param sql SQL Template
222      * @param rs Recordset with at least one record - there must be
223      * a current record
224      * @return SQL command with replaced values
225      * @throws Throwable
226      */

227     protected String JavaDoc getSQL(String JavaDoc sql, Recordset rs) throws Throwable JavaDoc
228     {
229         
230         TemplateEngine t = new TemplateEngine(_ctx,_req, sql);
231         return t.getSql(rs);
232         
233     }
234     
235     /**
236      * Load the appropiate class and creates an object
237      * that MUST subclass GenericTransaction. This method is
238      * used by Transactions that delegate work on "subtransaction"
239      * objects. All these classes subclass GenericTransaction to inherit all the
240      * code supporting business logic programming. You may define your
241      * own methods in those classes, they are intended to refactor
242      * common business code that may be used by multiple Transactions.<br>
243      * Typically, you will use code like this:<br>
244      * <pre>
245      * MyOwnClass obj = (MyOwnClass)getObject("mypackage.MyOwnClass");
246      * obj.myMethod();
247      * </pre>
248      * <br>
249      * An object created this way inherits all the power of
250      * the GenericTransaction, including the availability of
251      * security information (current user), access to the same
252      * database connection as the caller, etc. Both objects participate
253      * in the same JDBC Transaction if this feature was enabled in
254      * the config.xml file.
255      *
256      * @param className Name of the class to instantiate
257      * @return An object of class GenericTransaction
258      * @throws Throwable
259      */

260     protected GenericTransaction getObject(String JavaDoc className) throws Throwable JavaDoc
261     {
262
263         GenericTransaction t = null;
264
265         /* load transaction class */
266         t = (GenericTransaction) Thread.currentThread().getContextClassLoader().loadClass(className).newInstance();
267         t.init(_ctx, _req, _res);
268         t.setConfig(_config);
269         t.setConnection(_conn);
270                     
271         /* log jdbc performance? */
272         t.setLogWriter(_pw);
273
274         return t;
275         
276     }
277     
278     /**
279      * Create a recordset with all the fields
280      * required to produce a chart with ChartOutput. This recordset
281      * will contain no records.
282      * @return Recordset with the column structure required by
283      * the class ChartOutput
284      * @throws Throwable
285      */

286     public Recordset getChartInfoRecordset() throws Throwable JavaDoc
287     {
288         /* define chart params recordset */
289         Recordset rs = new Recordset();
290         rs.append("chart-plugin", java.sql.Types.VARCHAR);
291         rs.append("title", java.sql.Types.VARCHAR);
292         rs.append("title-x", java.sql.Types.VARCHAR);
293         rs.append("title-y", java.sql.Types.VARCHAR);
294         rs.append("column-x", java.sql.Types.VARCHAR);
295         rs.append("column-y", java.sql.Types.VARCHAR);
296         rs.append("title-series", java.sql.Types.VARCHAR);
297         rs.append("width", java.sql.Types.INTEGER);
298         rs.append("height", java.sql.Types.INTEGER);
299         rs.append("data", java.sql.Types.VARCHAR);
300         rs.append("dateformat", java.sql.Types.VARCHAR);
301         
302         //added on april-06-2004
303
rs.append("session", java.sql.Types.VARCHAR); //true|false: save in session?
304
rs.append("image-id", java.sql.Types.VARCHAR);//session attribute id
305

306         //added on july-19-2005
307
rs.append("color", java.sql.Types.VARCHAR); //true|false: save in session?
308

309         return rs;
310     }
311
312     /**
313      * Return DataSource object using JNDI prefix
314      * configured in web.xml context parameter. This is an
315      * utility method to help simplify Transaction code. A DataSource
316      * can be obtained with a single line of code:<br><br>
317      * <pre>
318      * javax.sql.DataSource ds = getDataSource("jdbc/customersDB");
319      * setConnection(ds.getConnection);
320      * ....
321      * </pre>
322      * <br>
323      * Remember that when you use your own datasource, you
324      * must close the connection in your Transaction code! consult
325      * the reference guide ("Sample code" section) for more information.
326      *
327      * @param name Name of the datasource (Example: jdbc/customersdb)
328      * @return DataSource object
329      * @throws Throwable If DataSource cannot be obtained
330      */

331     protected DataSource JavaDoc getDataSource(String JavaDoc name) throws Throwable JavaDoc
332     {
333
334       //get datasource config from web.xml
335
String JavaDoc jndiPrefix = "";
336       if (getContext()!=null)
337         jndiPrefix = getContext().getInitParameter("jndi-prefix");
338       else
339         jndiPrefix = "java:comp/env/";
340       
341       if (jndiPrefix==null)
342         jndiPrefix="";
343       
344       DataSource JavaDoc ds = Jndi.getDataSource(jndiPrefix + name);
345       if (ds==null)
346         throw new Throwable JavaDoc("Can't get datasource: " + name);
347         
348       return ds;
349             
350     }
351
352     /**
353      * Return the default application DataSource object
354      * as configured in web.xml context parameters. This is a
355      * utility method to help simplify Transaction code. A DataSource
356      * can be obtained with a single line of code:<br><br>
357      * <pre>
358      * javax.sql.DataSource ds = getDataSource();
359      * setConnection(ds.getConnection());
360      * ....
361      * </pre>
362      * <br>
363      * Remember that when you use your own datasource, you
364      * must close the connection in your Transaction code! please consult
365      * the reference guide ("Sample code" section) for more information.
366      *
367      * @return DataSource object
368      * @throws Throwable If DataSource cannot be obtained
369      */

370     protected DataSource JavaDoc getDataSource() throws Throwable JavaDoc
371     {
372
373       //get datasource config from web.xml
374
String JavaDoc jndiPrefix = null;
375       String JavaDoc name = null;
376       
377       if (getContext()!=null)
378       {
379         
380         if (getConfig().transDataSource!=null)
381             name = getConfig().transDataSource;
382         else
383             name = getContext().getInitParameter("def-datasource");
384             
385         jndiPrefix = getContext().getInitParameter("jndi-prefix");
386         
387       }
388       else
389         throw new Throwable JavaDoc("This method can't return a datasource if servlet the context is null.");
390       
391       if (jndiPrefix==null)
392         jndiPrefix="";
393       
394       DataSource JavaDoc ds = Jndi.getDataSource(jndiPrefix + name);
395       if (ds==null)
396         throw new Throwable JavaDoc("Can't get datasource: " + name);
397         
398       return ds;
399             
400     }
401
402     /**
403     * Returns an "env-entry" value stored in web.xml.
404     * @param name env-entry-name element
405     **/

406     protected String JavaDoc getEnvEntry(String JavaDoc name) throws Throwable JavaDoc
407     {
408         
409         Context env = (Context) new InitialContext().lookup("java:comp/env");
410         String JavaDoc v = (String JavaDoc) env.lookup(name);
411         return v;
412
413     }
414     
415     /**
416      * Retrieve internal HashMap containing all published Recordsets
417      * in case some output module needs to serialize this object
418      * or anything else
419      * @return HashMap containing all published Recordsets
420      */

421     public HashMap JavaDoc getData()
422     {
423         return _publish;
424     }
425     
426     /**
427      * Utility method to retrieve a recordset from a different data source
428      * than the one used by the action
429      * @param DataSourceName Data Source name like "jdbc/xxxx"
430      * @param sql SQL command that returns a result set
431      * @param limit The maximum number of rows to retrieve (0 = no limit)
432      * @return
433      * @throws Throwable
434      */

435     protected Recordset dbGet(String JavaDoc DataSourceName, String JavaDoc sql, int limit) throws Throwable JavaDoc
436     {
437         java.sql.Connection JavaDoc conn = getDataSource(DataSourceName).getConnection();
438         try
439         {
440             Db db = new Db(conn);
441             if (limit>0)
442                 return db.get(sql, limit);
443             else
444                 return db.get(sql);
445         }
446         catch (Throwable JavaDoc e)
447         {
448             throw e;
449         }
450         finally
451         {
452             if (conn!=null)
453                 conn.close();
454         }
455     }
456     
457     /**
458      * Utility method to retrieve a recordset from a different data source
459      * than the one used by the action
460      * @param DataSourceName Data Source name like "jdbc/xxxx"
461      * @param sql SQL command that returns a result set
462      * @return
463      * @throws Throwable
464      */

465     protected Recordset dbGet(String JavaDoc DataSourceName, String JavaDoc sql) throws Throwable JavaDoc
466     {
467         return dbGet(DataSourceName, sql, 0);
468     }
469     
470     /**
471      * Creates a recordset according to a structure defined in a
472      * flat file. The 1st line defines the column types, the second line
473      * defines the column names. From 3rd line begins data records. Columns
474      * are separated by TAB, rows are separated by CR+NL.
475      * @param path Path to flat file defining recordset structure and data. If path starts with "/..." it is interpreted as a location relative
476      * to the context, otherwise it is assumed to be located in the Action's path.
477      * @return Recordset according to the flat file structure
478      * @throws Throwable
479      */

480     protected Recordset getRsFromFlatFile(String JavaDoc path) throws Throwable JavaDoc
481     {
482             Recordset rs = new Recordset();
483             String JavaDoc data = getResource(path);
484             String JavaDoc rows[] = StringUtil.split(data, "\r\n");
485             String JavaDoc listseparator = "\t";
486
487             //adjust which list separator is used, tab or comma or semicolon
488
if (rows[0].indexOf(",") != -1)
489                     listseparator = ",";
490             else
491             if (rows[0].indexOf(";") != -1)
492                     listseparator = ";";
493
494             String JavaDoc fields[] = StringUtil.split(rows[0], listseparator);
495             String JavaDoc names[] = StringUtil.split(rows[1], listseparator);
496             boolean is_blank_row = false;
497
498             if (fields.length!=names.length)
499                     throw new Throwable JavaDoc("Row #2 (column names) does not match the right number of columns.");
500
501             for (int i=0;i<fields.length;i++)
502             {
503                     if (fields[i].toLowerCase().equals("varchar"))
504                             rs.append(names[i], java.sql.Types.VARCHAR);
505                     else if (fields[i].toLowerCase().equals("date"))
506                             rs.append(names[i], java.sql.Types.DATE);
507                     else if (fields[i].toLowerCase().equals("integer"))
508                             rs.append(names[i], java.sql.Types.INTEGER);
509                     else if (fields[i].toLowerCase().equals("double"))
510                             rs.append(names[i], java.sql.Types.DOUBLE);
511                     else {
512                             throw new Throwable JavaDoc("Invalid column type [" + fields[i] +"]. Valid column types are: varchar, date, integer and double.");
513                     }
514
515             }
516
517             for (int i=2;i<rows.length;i++)
518             {
519                     //here is a empty line
520
if(rows[i].equals(""))
521                             continue;
522
523                     //add a record if not all of last row is null,flatfile recordset does not allow all fields is null
524
if(!is_blank_row)
525                         rs.addNew();
526
527                     //initial flag
528
is_blank_row = true;
529
530                     String JavaDoc value[] = StringUtil.split(rows[i], listseparator);
531
532                     //if (fields.length!=value.length)
533
// throw new Throwable("Row #" + i + " does not match the right number of columns.");
534

535                     for (int j=0;j<Math.min(fields.length,value.length);j++)
536                     {
537                             //replace lables such as ${lbl:},${ses:},%{req:}
538
value[j] = getSQL(value[j],null);
539
540                             //if this field is null,then set field null.if all fields is null,is_blank_row will equal true.
541
if(value[j].equals("")||value[j].toLowerCase().equals("null"))
542                             {
543                                     rs.setValue(names[j], null);
544                             }
545                             else
546                             {
547                                     is_blank_row = false;
548                                     if (fields[j].toLowerCase().equals("varchar"))
549                                             rs.setValue(names[j], value[j]);
550                                     else if (fields[j].toLowerCase().equals("date"))
551                                     {
552                                             if(value[j].indexOf("@") != -1) //formated date value
553
{
554                                                     String JavaDoc date[] = StringUtil.split(value[j], "@");
555                                                     rs.setValue(names[j], StringUtil.getDateObject(date[0],date[1]));
556                                             }
557                                             else
558                                             {
559                                                     rs.setValue(names[j], StringUtil.getDateObject(value[j],"yyyy-MM-dd"));
560                                             }
561                                     }
562                                     else if (fields[j].toLowerCase().equals("integer"))
563                                             rs.setValue(names[j], new Integer JavaDoc(value[j]));
564                                     else if (fields[j].toLowerCase().equals("double"))
565                                             rs.setValue(names[j], new Double JavaDoc(value[j]));
566                             }
567                     }
568             }
569
570             //remove the last row if all field is null
571
if(is_blank_row)
572                     rs.delete(rs.getRecordNumber());
573
574             return rs;
575     }
576     
577 }
578
Popular Tags