You are viewing a plain text version of this content. The canonical link for it is here.
Posted to torque-dev@db.apache.org by Alan Hodgkinson <al...@softxs.ch> on 2003/01/10 11:28:53 UTC

Suggestion: A change to Torque (populate & XML)

<cocoon-user-note>
  To Cocoon users: Sorry for the off-topic post. 
  This is just a heads up to let you know what _may_ 
  be coming and to solicit your lobbying efforts for 
  the Torque changes I am proposing below.
</cocoon-user-note>

Dear All,

I'd like to have some new functionality in Torque. My 
ultimate goal is to implement the enhancements necessary 
to make Torque a useful component when developing with
Cocoon.

<disclaimer>
  I'm not hung up in implementing the changes exactly
  as suggested here. I've written the following mainly 
  to clarify my ideas and solicit better suggestions 
  from those who know more. That said, I've tried to 
  keep things simple by separating my requirements from 
  the solution and suggesting a 'small-footprint' change 
  that I can quickly get up and running.

  I just want a solution for my requirements and I'm 
  willing to write and test the code and DOCUMENT 
  whatever we collectively decide to do. But, barring
  other suggestions, my analysis thus far shows that 
  I do require changes to Torque (and I'm not a Torque 
  commiter). 

  On the non-technical side, I'd like the community,
  particularly the Cocoon users (who will get a new tool 
  for their toolbox), to share the benefits. After all, 
  the Torque-ers have already helped me.
</disclaimer>


1. REQUIREMENTS
===============

1.1 Populate Torque objects, e.g. objects which extend 
   BaseObject, automatically from a Map or Hashtable. 
   E.g.:

     // users is an instance of a Users Torque object
     // generated by Torque.

     users.populate( myHashFromMyCgiInput );
     if ( user.isValid() ) { // isValid is business logic
       users.save();
     } else {
       throw new BadUserInputException(); // or whatever..
     }

   Ideally the populate() method would accept String 
   representations of fields and convert them to the 
   appropriate native types (as well as accepting native 
   types).

1.2 Allow Torque objects to emit SAX events to a
   Content handler. 

   Note: most Cocoon processing is done via SAX events.

     // myContentHandler is a Cocoon transformer that
     // ultimately transforms it's input into fancy 
     // HTML suitable for web browser presentation.
     // It has been opened and a document has already 
     // been started

     myUser1.toXml( myContentHandler );
     myUser2.toXml( myContentHandler );
     myCompany1.toXml( myContentHandler );

     // ..etc.. until done.. then close the document.

   The toXml() method merely emits the tags and content 
   associated with to it's database record. E.g. the 
   table name '<user>' and the contained fields, 
   '<Userid>1</Userid><Username>joe</Username>' etc..

   Note: If you accept this, then you'll get XML as 
   Strings more or less for free. (And you'll get free 
   Steak Knives too.. :)


2.0 POSSIBLE SOLUTION
=====================

<another-disclaimer>
  Read the first disclaimer again: I'm open to other,
  better solutions.
</another-disclaimer>

2.1 Populate()

  With a bit of Java (and personal :) introspection and some 
  helpful suggestions from the list members, I feel I can 
  implement the populate method. The tricky part will be
  getting all the type conversion working.

  This task would be made MUCH easier if there were a 
  method getFieldNames(), that returns null (or throws an 
  exception) in BaseObject. It would be over-ridden by all 
  the Torque generated classes). 

  This would allow applications to use Torque objects in a 
  generic way. They could simply iterate over all the fields, 
  using BaseObject's (overridden) getByName method.

2.2 Sax events

  Given the getFieldNames method in BaseObject, it becomes
  a trivial exercise to implement the SAX event generation
  directly in BaseObject. It is would also be trivial to 
  implement a toXml method, that returned the XML as a
  String. (..and you'd get free Steak Knives too. :)

  The only problem is to get the table name (for placing in
  the top-level XML tag). The easiest solution would be to 
  add a null (or exception-throwing) method getTableName to 
  BaseObject, which would be overridden by the generated 
  Torque objects.

  Note: since I pan to use Cocoon, I have no need of filtering 
  or forcing the XML to conform to any particular layout. 
  Cocoon allows me to easily use XSLT to perform any required 
  transformation(s).


3.0 SUMMARY OF CHANGES
======================

3.1 Add to BaseObject.java:

    public List getFieldNames() { return null; }
    public String getTableName() { return null; }
  
  They could alternatively throw a 'not implemented' 
  exception the way getByName currently does.

    void populate( Map or Hashtable ) { ... }
    void toXml( ContentHandler ) { ... }
    String toXml( ) { ... }
      or toXml( Writer ) { ... }

  I'll supply tested code with Javadoc for these methods.

3.2 Add to Object.vm:

    public String getTableName() { return "$table.Name"; }

  I'll need to test to ensure that the Torque/velocity variable
  name is correct.

3.3 Collect the free Steak Knives :)

   Sorry, but we're all out of Steak Knives :(. 

   ..but I will write a few paragraphs of documentation for 
   the Torque web site and some Cocoon Wiki documentation when 
   I get this working with Cocoon. (That's BETTER than steak 
   knives, right? :)


4.0 AN APPEAL
=============

4.1 Is there a Torque committer willing to 'sponsor me'?
  No, not to sponsor me to become a commiter, just to commit 
  the changes.

4.2 I'll supply the changes in whatever format is easiest.


Best wishes and thanks in advance (especially for reading 
this far),

Alan.

P.S. 

<thanks>
  Nothing happens in a vacuum: Others have helped. In particular:
    Michael Bain - For sharing your introspection based solution.
    Gareth Boden - For sharing your Betwixt based solution and 
      giving me a bunch of insights.
  Without your efforts, I would have been stuck. Thanks guys!
</thanks>

Re: Suggestion: A change to Torque (populate & XML)

Posted by Stephen Haberman <st...@beachead.com>.
On Mon, Jan 13, 2003 at 10:19:39AM +0000, Alan Hodgkinson wrote:
> 
> > {Use] ..Intake.. [for populating your objects].
> 
> Thanks. I hadn't seen that. Exactly how 'fragile' is it?

It is great once you have it set up (e.g. the XML file with the
rules you want and what not), but getting it that initial
point where everything clicks and works can be tedious. I had
problems with it anyway, and there have been several other people
posting with the same general problems, e.g. minor configuration
errors they couldn't debug, but once pointed out, it worked as
advertised.

- Stephen

Re: Suggestion: A change to Torque (populate & XML)

Posted by Gareth Boden <ga...@egsgroup.com>.
>> You can get field names by doing:
>> Torque.getDatabaseMap().getTable(XxxPeer.TABLE_NAME);
> <snip/>
>> ..if you just have Xxx, you can do Xxx.getPeer().TABLE_NAME.
>
> I just have Xxx, which in my case, is of type BaseObject.
> Unfortunately, BaseObject doesn't define a stub for getPeer, so
> my code won't even complile. Note that I use BaseObject
> because I need my code to be generic and can't be coding for
> a particular database table.

Simple reflection can sort that, something along the lines of (without 
having the exact method names to hand) :
	Method getPeer = Xxx.getClass().getMethod("getPeer", new Class[0]);
	Object peer = getPeer.invoke(Xxx);
	Field tableNameField = peer.getClass().getField("TABLE_NAME");
	Object tableName = tableNameField.getValue();

However, I believe I'm right in thinking that the DatabaseMap does not 
concern itself with the method names on the data objects - so although 
you can find all the column names for a table, you still can't find 
their values on an arbitrary object in order to turn it to XML. I could 
be wrong... but if I'm not, something new still has to go into the 
generated code.

G.


Re: Suggestion: A change to Torque (populate & XML)

Posted by Gareth Boden <ga...@egsgroup.com>.
>> You can get field names by doing:
>> Torque.getDatabaseMap().getTable(XxxPeer.TABLE_NAME);
> <snip/>
>> ..if you just have Xxx, you can do Xxx.getPeer().TABLE_NAME.
>
> I just have Xxx, which in my case, is of type BaseObject.
> Unfortunately, BaseObject doesn't define a stub for getPeer, so
> my code won't even complile. Note that I use BaseObject
> because I need my code to be generic and can't be coding for
> a particular database table.

Simple reflection can sort that, something along the lines of (without 
having the exact method names to hand) :
	Method getPeer = Xxx.getClass().getMethod("getPeer", new Class[0]);
	Object peer = getPeer.invoke(Xxx);
	Field tableNameField = peer.getClass().getField("TABLE_NAME");
	Object tableName = tableNameField.getValue();

However, I believe I'm right in thinking that the DatabaseMap does not 
concern itself with the method names on the data objects - so although 
you can find all the column names for a table, you still can't find 
their values on an arbitrary object in order to turn it to XML. I could 
be wrong... but if I'm not, something new still has to go into the 
generated code.

G.


Re: Suggestion: A change to Torque (populate & XML)

Posted by Alan Hodgkinson <al...@softxs.ch>.
Dear Stephen,

Thanks for your tips regarding the accessing of table and field
names. You suggestions were helpful and almost works in my case.

> {Use] ..Intake.. [for populating your objects].

Thanks. I hadn't seen that. Exactly how 'fragile' is it?
 
> You can get field names by doing:
> Torque.getDatabaseMap().getTable(XxxPeer.TABLE_NAME);
<snip/> 
> ..if you just have Xxx, you can do Xxx.getPeer().TABLE_NAME.

I just have Xxx, which in my case, is of type BaseObject. 
Unfortunately, BaseObject doesn't define a stub for getPeer, so
my code won't even complile. Note that I use BaseObject 
because I need my code to be generic and can't be coding for 
a particular database table.

Failing some other better idea, could we add a 'not-implemented 
exception throwing' stub for getPeer to BaseObject.java?

[I can't help feeling that I'm going run into other problems 
caused by BaseObject not defining a 'non-implemented' stub for 
the methods that are generated in the XxxBase objects. ]

> As far as the toXml method, I'd rather not see the code go into
> Torque, especially the generation templates, as Torque is complex
> enough as it is.

Your choice. I'll work up a separate class, most likely using 
some of your and the others suggestions (BeanUtils, Castor, etc.)

That aside, and having looked at the code, I am pleasently surprised 
to see how simple it really is. This is not to take anything away 
from Torque, just merely to say that the code seems reasonably 
well organized and easy to read.

BEst wishes,

Alan.

P.S. The entire diff to implement the table name method and the
field name list is only:

Object.vm:
851c851
<     public synchronized List getFieldNames()
---
>     public static synchronized List getFieldNames()
1492,1501d1491
<     }
< 
<     /** 
<       * Get the database table name associate with this 
<       * class: E.g.: $table.Name 
<       * @return the database table name.
<       */
<     public String getTableName() 
<     { 
<         return "$table.Name"; 


BaseObject.java
61d60
< import java.util.List;
339,385d336
<     }
< 
<     /**
<      * Retrieves a list of the field names from the object. 
<      * Must be overridden if called.
<      * BaseObject's implementation will throw an Error.
<      *
<      * @return list of field names.
<      *
<      */
<     public synchronized List getFieldNames() { 
<         throw new Error("BaseObject.getFieldNames: " +
NOT_IMPLEMENTED);
<     }
< 
<     /**
<      * Retrieves the object's database table.  
<      * Must be overridden if called.
<      * BaseObject's implementation will throw an Error.
<      *
<      * @return list of field names.
<      *
<      */
<     public String getTableName()
<     {   // We'd like this to be static, but then it cannot be
overridden.
<         throw new Error("BaseObject.getTableName: " +
NOT_IMPLEMENTED);
<     }

--end--

Re: Suggestion: A change to Torque (populate & XML)

Posted by Stephen Haberman <st...@beachead.com>.
On Fri, Jan 10, 2003 at 07:13:23PM +0200, Andreou Andreas wrote:
> How about letting an external tool handle the object->XML conversion 
> (i.e. castor) ???
> We could then just have torque generate the Mapping File for the BaseXXX 
> classes ...

I don't know much about Castor, but it sounds like a good solution.

- Stephen

Re: Suggestion: A change to Torque (populate & XML)

Posted by Andreou Andreas <an...@di.uoa.gr>.
How about letting an external tool handle the object->XML conversion 
(i.e. castor) ???
We could then just have torque generate the Mapping File for the BaseXXX 
classes ...



Re: Suggestion: A change to Torque (populate & XML)

Posted by Stephen Haberman <st...@beachead.com>.
On Fri, Jan 10, 2003 at 10:28:53AM +0000, Alan Hodgkinson wrote:
> 1.1 Populate Torque objects, e.g. objects which extend 
>    BaseObject, automatically from a Map or Hashtable. 

We've already got a way to do this, plus it does validation and is
pretty darn spiff (though a bit fragile). Intake lives in both
Fulcrum (the fragile version I'm familiar with) and also in
Turbine-2 (with a recently refactored version by...Quinton, if I
remember right, that hopefully is a lot better than what is in
Fulcrum).

See:

http://jakarta.apache.org/turbine/fulcrum/howto/intake-service.html

> 1.2 Allow Torque objects to emit SAX events to a
>    Content handler. 
<snip/>
>   This task would be made MUCH easier if there were a 
>   method getFieldNames(), that returns null (or throws an 
>   exception) in BaseObject. It would be over-ridden by all 
>   the Torque generated classes). 

You can get field names by doing:

Torque.getDatabaseMap().getTable(XxxPeer.TABLE_NAME);

With the TableMap, you can get all of the columns and what not as
you were wanting to. (Although the
DatabaseMap/TableMap/ColumnMap...might only be available at runtime,
not compile/generation time, which might not be what you wanted).

> 2.2 Sax events
> 
>   Given the getFieldNames method in BaseObject, it becomes
>   a trivial exercise to implement the SAX event generation
>   directly in BaseObject. It is would also be trivial to 
>   implement a toXml method, that returned the XML as a
>   String. (..and you'd get free Steak Knives too. :)
> 
>   The only problem is to get the table name (for placing in
>   the top-level XML tag). The easiest solution would be to 
>   add a null (or exception-throwing) method getTableName to 
>   BaseObject, which would be overridden by the generated 
>   Torque objects.

Again, XxxPeer.TABLE_NAME should work. Also, if you just have Xxx,
you can do Xxx.getPeer().TABLE_NAME.

As far as the toXml method, I'd rather not see the code go into
Torque, especially the generation templates, as Torque is complex
enough as it is.

Unless you do something like XmlConverter which as a static
toXml(BaseObject obj) method to which uses the runtime-available
XxxMap stuff to spit out XML, either was a String or events or what
not, we could drop it in a util directory.

- Stephen