Could you show some example code of your services you are using from Cocoon?
Oh, is this OJB or Cocoon question? :)

Sure, part of it is a time tracking application where employees can clock in and clock out, here is the clock in and clock out flow:


function pub_clockIn()
{
    checkAccess()
    time.clockIn(user.uid);
    showTimeTrackerHome();
}

function pub_clockOut()
{
    checkAccess()
    var form = new Form("forms/timeTrackerClockOut.xml");
    var model = form.getModel();
    model.lunch = time.getAverageLunchFor(user.uid);
    form.showForm("TimeTrackerClockOut", {"user": user});
    time.clockOut(user.uid, model.lunch);
    showTimeTrackerHome();
}

the global time variable (time.clockIn(...) time.clockOut(...)) is one of the services accessing OJB. My services are populated into the global scope when the user successfully logs in (which authenticates against an ldap, using a different service). The global user variable is populated on successful login as well and is basically a caching wrapper around an ldap InetOrgPerson, the uid is treated as a unique identifier for related database lookups (the time tracking system uses a database via OJB, the user management an ldap via JNDI).


The time service looks like:


public class TimeService
{
    private TimeRepository rep = Factory.getTimeRepository();

    /**
     * Create a new Entry with a start time of now
     * @param uid
     * @return
     */
    public Entry clockIn(String uid)
    {
        Transaction tx = null;
        try
        {
            tx = rep.currentTransaction();
            tx.begin();
            Account account = rep.findAccountFor(uid);
            Entry entry = account.clockIn();
            return entry;
        }
        catch (Exception e)
        {
            tx.rollback();
            return null;
        }
        finally
        {
            if (tx != null && tx.isInProgress()) tx.commit();
        }
    }

    public Entry clockOut(String uid, double lunch)
    {
        Transaction tx = null;
        try
        {
            tx = rep.currentTransaction();
            tx.begin();

            Account account = rep.findAccountFor(uid);
            Entry e = account.clockOut();
            e.setLunch(lunch);
            return e;
        }
        finally
        {
            if (tx != null && tx.isInProgress()) tx.commit();
        }
    }

    public BigDecimal getAverageLunchFor(String uid)
    {
        Account a = findAccountFor(uid);
        Iterator entries = a.getEntries().iterator();
        int count = 0;
        double total = 0;
        while (entries.hasNext() && count++ < 10)
        {
            Entry entry = (Entry) entries.next();
            total += entry.getLunch();
        }
        return new BigDecimal(total / count);
    }

...
}

The TimeRepository is a gateway to the persistence services (the OJB stuff) and is used by the time service to find persistent classes. The service delegates most thinking to the classes it retrieves (this isn't factored perfectly at the moment).

public class TimeRepository extends Repository
{
    public Account findAccountFor(String uid)
    {
        OTMConnection conn = null;
        Transaction tx = null;
        boolean auto = false;
        try
        {
            conn = getBroker().getConnection();
            tx = getBroker().currentTransaction();
            auto = !tx.isInProgress();
            if (auto) tx.begin();

OQLQuery query = conn.newOQLQuery();
query.create("select accounts from " + Account.class.getName() + " where uid = $1");
query.bind(uid);


            Iterator itty = conn.getIteratorByOQLQuery(query);
            Account a = itty.hasNext() ? (Account) itty.next() : null;
            return a;
        }
        catch (Exception e)
        {
            System.err.println(e.getMessage());
            e.printStackTrace();
            tx.rollback();
        }
        finally
        {
            if (auto && tx != null && tx.isInProgress()) tx.commit();
            if (auto && conn != null && !conn.isClosed()) conn.close();
        }
        return null;
    }

which extends a general repository used as a base to provide persistence services to a number of repositories =)

public class Repository
{
    private static final  OTMBroker broker = new OTMBroker();

    public OTMBroker getBroker()
    {
        return broker;
    }

    public Transaction currentTransaction()
    {
        return broker.currentTransaction();
    }

    public void clearCache()
    {
        broker.clearCache();
    }
}

It uses an OTMBroker whose only real role is to handle the transaction-per-thread semantics we use:

public class OTMBroker
{
    private Kit kit;
    private PBKey key;
    private ThreadLocal conn;

    public OTMBroker()
    {
        conn = new ThreadLocal();
        this.kit = SimpleKit.getInstance();
        this.key = PersistenceBrokerFactory.getDefaultKey();
    }

public OTMConnection getConnection()
{
if (conn.get() == null || ((OTMConnection)conn.get()).isClosed() )
{
conn.set(kit.acquireConnection(key));
}
return (OTMConnection) conn.get();
}


    public Transaction currentTransaction()
    {
        return kit.getTransaction(getConnection());
    }

public void clearCache()
{
PersistenceBroker broker = PersistenceBrokerFactory.defaultPersistenceBroker();
broker.clearCache();
broker.close();
}
}


The Account and Entry classes are persistent classes and they contain all of the real logic. You could probably combine the Repository and OTMBroker classes as they are tightly coupled, but I am a "lots of small classes with clear responsibilities" kind of guy. The "get average lunch for user" functionality could easily be pushed back to the Account class, but it is really just a user interface thing as the only reason the functionality is there is so that the form can be pre-populated, it isn't used anywhere else.

The cache clearing stuff is just there for unit testing.

-Brian

On Jan 21, 2004, at 11:18 AM, mirko wrote:

Brian McCallister wrote:

btw - it may be worth moving this discussion to [EMAIL PROTECTED] at this point =)

OK. But at this moment I don't have more OJB specific questions. If I have I will ask there.


On the Cocoon block and using OJB from flow, etc:
As mentioned, I use OJB in two cocoon apps I am working on. One is in production (woo hoo!) and the other is under development. In both cases I do not expose OJB directly to Cocoon. I provide a set of services into application functionality and make these available from flow. The services use OJB. Remember that Cocoon, really, is just a big servlet so you can do any Java stuff you want to in it.

I think I will do similiar and as the FAQ suggests I will break my model into entities and business classes and call them from Cocoon's flow. Only business classes will eventually handle OJB specific method calls.
Could you show some example code of your services you are using from Cocoon?
Oh, is this OJB or Cocoon question? :)


Regards,
mirko

---------------------------------------------------------------------
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