Here we go, another way to do this, from Turbine-user list, thanks to Scott
Eade:

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.

Cheers,

Scott

------------------- extend-user-howto.xml -----------------------

<?xml version="1.0"?>
 <document>
  <properties>
    <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>

  <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>
      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>
      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>
      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();

    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.TurbineMapBuilderAdap
ter
 services.SecurityService.user.class=org.mycompany.newapp.om.NewappUser

services.SecurityService.userPeer.class=org.mycompany.newapp.om.NewappUserPe
er
    ]]></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[
        NewappUser user = (NewappUser) data.getUser();
    ]]></source>
     <p>
      Extending TurbineUser should be relatively straightforward with the
help
      of this information.
    </p>
     <p>
      Enjoy.
    </p>
   </section>
</body>
</document>



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to