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]