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