You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@turbine.apache.org by dl...@apache.org on 2001/09/06 18:47:38 UTC
cvs commit: jakarta-turbine-2/xdocs/howto extend-user-howto.xml
dlr 01/09/06 09:47:38
Modified: xdocs/howto extend-user-howto.xml
Log:
Added patch by Scott B. Eade (generated by Dan A. Bachelder), with
minor modifications.
Revision Changes Path
1.2 +308 -92 jakarta-turbine-2/xdocs/howto/extend-user-howto.xml
Index: extend-user-howto.xml
===================================================================
RCS file: /home/cvs/jakarta-turbine-2/xdocs/howto/extend-user-howto.xml,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- extend-user-howto.xml 2001/08/23 11:25:09 1.1
+++ extend-user-howto.xml 2001/09/06 16:47:38 1.2
@@ -5,110 +5,326 @@
<title>Extend User Howto</title>
<author email="jon@latchkey.com">Jon S. Stevens</author>
<author email="dan@chowda.net">Dan A. Bachelder</author>
+ <author email="seade@backstagetech.com.au">Scott B. Eade</author>
</properties>
-
+
<body>
-
+
<section name="Extend User">
-
- <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[
+
+ <p>
+ This is a 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>
- <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 <code>init</code> 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: turbine-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: turbine-dev-help@jakarta.apache.org