Sure thing... this is my first patch ever... so someone might want to be
extra careful with it :)
I got the howto i wrote from web CVS and cut the new from the email and then
did this:
diff -u extend-user-howto.xml extend_user_howto_new.xml >>
extend-user-howto-patchfile.txt
look right to everyone?
----- Original Message -----
From: "Daniel Rall" <[EMAIL PROTECTED]>
To: <[EMAIL PROTECTED]>
Sent: Thursday, August 30, 2001 6:16 PM
Subject: Re: [HOWTO] Extend TurbineUser with your own class... - New Version
for review
> "Scott Eade" <[EMAIL PROTECTED]> writes:
>
> > Below is my completely revamped XDOC version of extend-user-howto.xml.
> >
> > Can those of you that are interested please have a look at this and let
me know
> > if you agree with my approach which is a combination of the approaches
put
> > forward by a number of different people on this list.
> >
> > The approach documented works, but I am not going to be too surprised if
> > there are different opinions as to the best way of doing this. It should
be noted
> > however that this is the only way I have been able to get this working
without
> > risking the loss of code when regenerating the OM layer code.
> >
> > I'll try out any suggestions and update the document before requesting a
> > commit.
>
> Please send a unidiff.
>
> http://jakarta.apache.org/site/source.html
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
>
>
--- extend-user-howto.xml Sun Sep 02 00:42:46 2001
+++ extend_user_howto_new.xml Sun Sep 02 00:40:00 2001
@@ -5,110 +5,323 @@
<title>Extend User Howto</title>
<author email="[EMAIL PROTECTED]">Jon S. Stevens</author>
<author email="[EMAIL PROTECTED]">Dan A. Bachelder</author>
+ <author email="[EMAIL PROTECTED]">Scott B. Eade</author>
</properties>
-
+
<body>
-
+
<section name="Extend User">
+
+ <p>
+ This is a not quite quick-and-dirty HOWTO on extending the TurbineUser and
+ its functionality. The motivating factors for extending TurbineUser are:
+ <ol>
+ <li>
+ to be able to make use of TURBINE_USER.USER_ID as a foreign key in
+ application tables; and
+ </li>
+ <li>
+ to be able to represent additional user attributes by adding columns
+ to TURBINE_USER.
+ </li>
+ </ol>
+ </p>
+ <p>
+ The example herein uses TDK 2.1 and the TDK sample application Newapp.
+ To illustrate solutions to both of out motivators we will:
+ <ol>
+ <li>
+ Add a CREATE_USER_ID column to the RDF application table.
+ </li>
+ <li>
+ Add a TITLE column to TURBINE_USER.
+ </li>
+ </ol>
+ </p>
+ <p>
+ First we update the schema for the project (in this case
+ newapp-schema.xml) to include an alias definition of TurbineUser (which
+ will NOT create a new table) as well as the desired foreign key
+ references. Note how the TurbineUser definition refers to a pair of
+ adapter classes (that we will create shortly) and how it is only
+ necessary to define the columns we are referring to as foreign keys
+ elsewhere in the application database. Note also the addition of
+ CREATE_USER_ID as a foreign key in the RDF table.
+ </p>
+ <source><![CDATA[
+ <table name="NEWAPP_USER" javaName="NewappUser" alias="TurbineUser"
+ baseClass="org.mycompany.newapp.om.TurbineUserAdapter"
+ basePeer="org.mycompany.newapp.om.TurbineUserPeerAdapter">
+ <!-- Unique identifier -->
+ <column name="USER_ID" primaryKey="true" required="true" type="integer"/>
+ </table>
- <p>
- This is a quick a dirty HOWTO on extending the TurbineUser and
- its functionality. <br/>
- All information is based on what I had to do to get this to work
- under TDK 2.1.
- </p>
-
- <p>
- Define your User in your [PROJECT]-schema.xml file:
- </p>
-
- <source><![CDATA[
-
- <table name="SCARAB_USER" javaName="ScarabUserImpl" alias="TurbineUser"
- baseClass="org.apache.turbine.om.security.TurbineUser"
- basePeer="org.apache.turbine.om.security.peer.TurbineUserPeer">
- <column name="USER_ID" primaryKey="true" required="true"
- type="INTEGER"/>
+ <table name="RDF" idMethod="autoincrement">
+ <column name="RDF_ID" required="true" autoIncrement="true"
+ primaryKey="true" type="INTEGER"/>
+ <column name="TITLE" size="255" type="VARCHAR"/>
+ <column name="BODY" size="255" type="VARCHAR"/>
+ <column name="URL" size="255" type="VARCHAR"/>
+ <column name="AUTHOR" size="255" type="VARCHAR"/>
+ <column name="DEPT" size="255" type="VARCHAR"/>
+ <column name="CREATE_USER_ID" required="true" type="INTEGER"/>
+ <foreign-key foreignTable="NEWAPP_USER">
+ <reference local="CREATE_USER_ID" foreign="USER_ID"/>
+ </foreign-key>
</table>
]]></source>
-
- <p>
- You only need to define the columns in which you are referring to as a
- foreign key elsewhere in your database. In our case, that is
- just USER_ID. <br/>
- This will NOT create a new table in your schema, it simply allows you to
- make foreign key references to the TURBINE_USER table in you applications
- schema.
- </p>
-
- <p>
- Any new columns need to be added to the TURBINE_USER def. in the
- turbine-schema.xml file.
- </p>
-
- <p>
- Create an interface which describes the additional methods you are adding
- to your User object:
- </p>
-
- <source><![CDATA[
- public interface ScarabUser extends User
+ <p>
+ The columns we want to add to TurbineUser must be defined in
+ turbine-schema.xml thus:
+ </p>
+ <source><![CDATA[
+ <table name="TURBINE_USER" idMethod="idbroker">
+ <column name="USER_ID" required="true" primaryKey="true" type="INTEGER"/>
+ <column name="LOGIN_NAME" required="true" size="32" type="VARCHAR"/>
+ <column name="PASSWORD_VALUE" required="true" size="32" type="VARCHAR"/>
+ <column name="FIRST_NAME" required="true" size="99" type="VARCHAR"/>
+ <column name="LAST_NAME" required="true" size="99" type="VARCHAR"/>
+ <column name="EMAIL" size="99" type="VARCHAR"/>
+ <column name="CONFIRM_VALUE" size="99" type="VARCHAR"/>
+ <column name="TITLE" size="99" type="VARCHAR"/> <!-- New column -->
+ <column name="MODIFIED" type="TIMESTAMP"/>
+ <column name="CREATED" type="TIMESTAMP"/>
+ <column name="LAST_LOGIN" type="TIMESTAMP"/>
+ <column name="OBJECTDATA" type="VARBINARY"/>
+ <unique>
+ <unique-column name="LOGIN_NAME"/>
+ </unique>
+ </table>
]]></source>
-
- <p>
- Using TDK 2.1 "ant init" will regenerate your OM layer, including the
- implementations referenced in the following paragraphs (ScarabUserImpl,
- ScarabUserImplPeer, BaseScarabUserImpl and BaseScarabUserImplPeer). <br/>
- <b>IT WILL ALSO RECREATE YOUR DATABASE TABLES DESTROYING ALL DATA
- THEREIN.</b>
- </p>
-
- <p>
- Have Torque create an implementation of ScarabUser. You should then fill
- it with the methods that you defined in ScarabUser. It should extend the
- Base class and look something like this:
- </p>
-
- <source><![CDATA[
- public class ScarabUserImpl extends BaseScarabUserImpl
- implements Persistent, ScarabUser
+ <p>
+ Before we create the adapter classes referred to above we will first
+ extend TurbineMapBuilder in order to tell Turbine about the additional
+ columns we are adding to TurbineUser. Note that you can actually omit
+ this step and not even define database columns for the additional
+ attributes you want to add if you don't care to have easy external access
+ to the data. If you do this the data for the additional attributes will
+ be written to TURBINE_USER.OBJECTDATA along with any other data added to
+ the the perm hashtable (this is a way cool feature, you should also look
+ into the temp hashtable if you like this).
+ </p>
+ <source><![CDATA[
+package org.mycompany.newapp.util.db.map;
+
+import java.util.Date;
+
+import org.apache.turbine.services.db.TurbineDB;
+import org.apache.turbine.util.db.map.TableMap;
+import org.apache.turbine.util.db.map.TurbineMapBuilder;
+
+public class TurbineMapBuilderAdapter extends TurbineMapBuilder
+{
+ public String getTitle()
+ {
+ return "TITLE";
+ }
+
+ public String getUser_Title()
+ {
+ return getTableUser() + '.' + getTitle();
+ }
+
+ public void doBuild()
+ throws java.lang.Exception
+ {
+ super.doBuild();
+
+ // Make some objects.
+ String string = new String("");
+ Integer integer = new Integer(0);
+ java.util.Date date = new Date();
+
+ // Add extra User columns.
+ TableMap tMap = TurbineDB.getDatabaseMap().getTable(getTableUser());
+ tMap.addColumn(getTitle(), string);
+ }
+}
]]></source>
-
- <p>
- You may need to add your interface (ScarabUser) to the implements
- statement manually.
- </p>
-
- <p>
- Have Torque create the appropriate Peer class (mine is empty so far):
- </p>
-
- <source><![CDATA[
- public class ScarabUserImplPeer
- extends org.tigris.scarab.om.BaseScarabUserImplPeer
- {
- }
+ <p>
+ Now we will implement the pair of adapters we referred to in our schema.
+ First we implement TurbineUserAdapter to provide access to the primary key
+ as well as the column we are adding to TurbineUser. If you are going to
+ use OBJECTDATA (and not define new columns in the database) you can still
+ add accessor methods here for convenience if you like, alternatively you
+ can just use setPerm() directly.
+ </p>
+ <source><![CDATA[
+package org.mycompany.newapp.om;
+
+import org.apache.turbine.om.security.TurbineUser;
+import org.apache.turbine.om.NumberKey;
+
+public class TurbineUserAdapter extends TurbineUser
+{
+ public static final String TITLE = "TITLE";
+
+ public NumberKey getUserId()
+ {
+ return (NumberKey) getPrimaryKey();
+ }
+
+ public void setTitle(String title)
+ {
+ setPerm(TITLE, title);
+ }
+
+ public String getTitle()
+ {
+ String tmp = null;
+ try
+ {
+ tmp = (String) getPerm(TITLE);
+ if ( tmp.length() == 0 )
+ tmp = null;
+ }
+ catch ( Exception e )
+ {
+ }
+ return tmp;
+ }
+}
]]></source>
+ <p>
+ Next comes TurbineUserPeerAdapter to which we also add details of the new
+ database columns (the body will be empty if you choose to use
+ OBJECTDATA).
+ </p>
+ <source><![CDATA[
+package org.mycompany.newapp.om;
+
+import java.util.Vector;
+
+import org.apache.turbine.om.security.peer.TurbineUserPeer;
+import org.mycompany.newapp.util.db.map.TurbineMapBuilderAdapter;
+
+public class TurbineUserPeerAdapter extends TurbineUserPeer
+{
+ private static final TurbineMapBuilderAdapter mapBuilder =
+ (TurbineMapBuilderAdapter) getMapBuilder();
- <p>
- In your TurbineResources.properties file modify the following properties
- to tell Turbine to point at your specific implementations of the User
- interface:
+ public static final String TITLE = mapBuilder.getUser_Title();
+}
+ ]]></source>
+ <p>
+ We can now use "ant project-om" to generate our OM layer using the adapter
+ classes we defined above. Note that "ant init" (WARNING: THE init TARGET
+ WILL DELETE ALL DATA IN YOUR DATABASE), or any other target that triggers
+ a compile of the OM layer will result in a compile error of BaseRdf due to
+ the fact that NewappUserPeer does not implement a retrieveByPK() method.
+ </p>
+ <p>
+ We can now use "ant project-om" to generate our OM layer using the adapter
+ classes we defined above. Note that "ant init" (WARNING: THE init TARGET
+ WILL DELETE ALL DATA IN YOUR DATABASE), or any other target that triggers
+ a compile of the OM layer will result in a compile error of BaseRdf due to
+ the fact that NewappUserPeer does not implement a retrieveByPK() method.
+ </p>
+ <p>
+ So lets implement retrieveByPK() so that everything can compile. This
+ needs to be implemented in NewappUserPeer which was generated by torque
+ when we executed project-om above (but it won't be deleted should we need
+ to regenerate the OM layer at some stage in the future - like in about 2
+ minutes).
+ </p>
+ <source><![CDATA[
+package org.mycompany.newapp.om;
+
+import java.util.*;
+
+import com.workingdogs.village.*;
+
+import org.apache.turbine.om.peer.*;
+import org.apache.turbine.util.*;
+import org.apache.turbine.util.db.*;
+import org.apache.turbine.util.db.map.*;
+import org.apache.turbine.util.db.pool.DBConnection;
+import org.apache.turbine.om.ObjectKey;
+import org.apache.turbine.services.db.TurbineDB;
+
+import org.mycompany.newapp.om.map.*;
+
+public class NewappUserPeer
+ extends org.mycompany.newapp.om.BaseNewappUserPeer
+{
+ public static NewappUser retrieveByPK(ObjectKey pk)
+ throws Exception
+ {
+ DBConnection db = null;
+ NewappUser retVal = null;
+
+ try
+ {
+ db = TurbineDB.getConnection(getMapBuilder()
+ .getDatabaseMap().getName());
+ retVal = retrieveByPK(pk, db);
+ }
+ finally
+ {
+ if (db != null)
+ {
+ TurbineDB.releaseConnection(db);
+ }
+ }
+ return(retVal);
+ }
+
+ public static NewappUser retrieveByPK( ObjectKey pk, DBConnection dbcon )
+ throws Exception
+ {
+ Criteria criteria = new Criteria();
+ criteria.add( USER_ID, pk );
+ Vector v = doSelect(criteria, dbcon);
+ if ( v.size() != 1)
+ {
+ throw new Exception("Failed to select one and only one row.");
+ }
+ else
+ {
+ return (NewappUser)v.firstElement();
+ }
+ }
+}
+ ]]></source>
+ <p>
+ Now we can now use "ant init" to generate the rest of the things it
+ generates - this will include the regenreation of the OM layer, the
+ generation of the sql to create the database tables and the actual
+ execution of this sql to recreate the database tables to now include any
+ additional columns we have defined (<b>AS A CONSEQUENCE ALL DATA IN THE
+ DATABASE WILL BE DESTROYED).</b>
+ </p>
+ <p>
+ With any luck everything will compile okay and we are only a small step
+ away from being able to use the new OM layer. The last step is to tell
+ Turbine about the new classes we are using for Users and the new
+ MapBuilder. To do this we need to update the following entries in
+ TurbineResources.properties:
+ </p>
+ <source><![CDATA[
+ database.maps.builder=org.mycompany.newapp.util.db.map.TurbineMapBuilderAdapter
+ services.SecurityService.user.class=org.mycompany.newapp.om.NewappUser
+ services.SecurityService.userPeer.class=org.mycompany.newapp.om.NewappUserPeer
+ ]]></source>
+ <p>
+ That is basically it. We can now modify our application to utilise the
+ new columns via the methods defined in the OM objects we have modified.
+ Note that in order to access the new methods in NewappUser we need to cast
+ from TurbineUser thus:
</p>
-
- <source><![CDATA[
- services.SecurityService.user.class=org.tigris.scarab.om.ScarabUserImpl
- services.SecurityService.userPeer.class=org.tigris.scarab.om.ScarabUserImplPeer
+ <source><![CDATA[
+ NewappUser user = (NewappUser) data.getUser();
]]></source>
-
- <p>
- As you can see, it isn't real hard to override Turbine's concept of a User
- with your own specific implementation. If you wish to rename column names
- and what not, then you will need to do a bit more work and is beyond the
- scope of this tutorial.
+ <p>
+ Extending TurbineUser should be relatively straightforward with the help
+ of this information.
</p>
-
- </section>
+ <p>
+ Enjoy.
+ </p>
+ </section>
</body>
</document>
-
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]