Persistence/OrderSystemExample/ModelBridge

Team ModelBridge of the OT/JPA example "Order-System" (role file of OrderSystemPersistenceTeam).

team package org.objectteams.samples.ordersystem.persistence.OrderSystemPersistenceTeam;

import java.util.Collection;

import org.objectteams.samples.ordersystem.data.Customer;
import org.objectteams.samples.ordersystem.order.StockOrder;
import org.objectteams.samples.ordersystem.store.StockItem;


/**
 * <p>
 * This role intercepts initialization of data and either reads data from DB if existent
 * or invokes the default behavior of creating hard coded initial objects.
 * </p><p>
 * Nested roles (subclasses of {@link EntityBridge} furthermore intercept
 * addition/removal of elements in order to persist these operations.
 * <p>
 * 
 * @author stephan
 */
protected team class ModelBridge playedBy ModelAdapterTeam
{
        /** Custom lifting constructor to further initialize this team/role. */
        public ModelBridge(ModelAdapterTeam modelAdapterTeam) {
                // when initializing this role (no later than at createOrRetrieveXYZ)
                // activate it for callins in nested roles to take effect.
                this.activate(ALL_THREADS);
        }
        
        // --- Accessors for instances of roles defined below: ---
        StorageBridge  getStorageBridge()  -> StorageAdapter<@base>  getStorageAdapter();
        CustomerBridge getCustomerBridge() -> CustomerAdapter<@base> getCustomerAdapter();
        OrderBridge    getOrderBridge()    -> OrderAdapter<@base>    getOrderAdapter();

        /**
         * This callin method generically handles requests to create a set of data of a given type.
         * <ul>
         * <li>If it can find corresponding entities in the given table it reads them from DB and
         *     feeds them into the given bridge thus making them accessible for the application</li>
         * <li>If no persistent entities are found it delegates back to the underlying creation
         *     (of hard-coded demo instances). It does so in a transaction so that all newly
         *     created objects will be committed in one step.</li>
         * </ul>
         * @param bridge this instance is used for accessing the DB table and for 
         *                                      feeding retrieved elements into the application.
         */
        @SuppressWarnings("basecall")
        callin <E> void createOrRetrieveElements(final EntityBridge<E> bridge) {
                simpleLog("checking for existing elements");
                Collection<E> allElements = bridge.findAllEntities();
                if (allElements != null && allElements.size() > 0) {
                        simpleLog("...retrieving elements");
                        this.deactivate(); // prevent recursion in callout below
                        try {
                                for (E element: allElements)
                                        bridge.addToApplication(element);
                                        // Technical note:
                                        //     Although this call leads to invoking XYZAdapter.add(E)
                                        //     which is also callin bound in role XYZBridge,
                                        //     this causes no re-entrance since that callin is not active
                                        //     due to the this.decativate() call above.
                        } finally {
                                this.activate();
                        }
                } else {
                        simpleLog("...creating initial elements");
                        inTransactionDo(new Runnable() { public void run() {
                                base.createOrRetrieveElements(bridge);
                        }});
                }
                simpleLog("done");
        }

        // ==== hook above callin method into several join points within the application:
        
        void createOrRetrieveElements(EntityBridge<StockItem> bridge) <- replace void fillStorage()
                with { bridge <- getStorageBridge() }
        
        void createOrRetrieveElements(EntityBridge<Customer> bridge) <- replace void createCustomers()
                with { bridge <- getCustomerBridge() }
        
        void createOrRetrieveElements(EntityBridge<StockOrder> bridge) <- replace void createOrders()
                with { bridge <- getOrderBridge() }

        
        /**
         * Generic gateway to an entity adapter (roles of base team 
         * {@link org.objectteams.samples.ordersystem.gui.ModelAdapterTeam}).
         * <br />
         * It serves as a two-way bridge:
         * <ul>
         * <li>DB -&gt; application --- using <code>addElement</code></li>
         * <li>application -&gt; -- using <code>persistNewElement, removeElement</code>, 
         *     to be invoked by callin triggers</li>
         * </ul>
         * Subclasses have to define method bindings for both directions.
         */
        abstract protected class EntityBridge<E>
        {
                /** Specifies the database table containing the entities managed by this bridge. */
                abstract String getTableName();
                
                /** Feed one element (assumably retrieved from DB) into the application. */
                abstract protected void addToApplication(E element);
                
                /** Retrieve all entities from the table specified by {@link #getTableName()} */
                protected Collection<E> findAllEntities() {
                        return OrderSystemPersistenceTeam.this.findAllEntities(getTableName());
                }
                
                static void persistNewElement(Object entity) {
                        simpleLog("persisting "+entity);
                        persistInTransaction(entity);
                }

                static void removeElement(Object entity) {
                        removeInTransaction(entity);
                }
                
        }
        
        /** Bind EntityBridge for StockItems managed by the StorageAdapter. */
        protected class StorageBridge extends EntityBridge<StockItem> playedBy StorageAdapter<@ModelBridge.base> 
        {
                String getTableName() { return "StockItem"; }
                
                void addToApplication(StockItem anItem) -> void add(StockItem anItem);

                persistNewElement       <- after  add;

                removeElement           <- before delete;
        }

        /** Bind EntityBridge for Customers managed by the CustomerAdapter. */
        protected class CustomerBridge extends EntityBridge<Customer> playedBy CustomerAdapter<@ModelBridge.base> 
        {
                String getTableName() { return "Customer"; }

                void addToApplication(Customer aCustomer) -> void addElement(Customer aCustomer);

                persistNewElement       <- after  addElement;

                removeElement           <- before removeElement;
        }

        /** Bind EntityBridge for StockOrders managed by the OrderAdapter. */
        protected class OrderBridge extends EntityBridge<StockOrder> playedBy OrderAdapter<@ModelBridge.base> 
        {
                String getTableName() { return "StockOrder"; }

                protected void addToApplication(StockOrder anOrder) {
                        internalAddElement(anOrder);
                        anOrder.init(); // one more action: orders must be initialized after retrieval.
                }
                void internalAddElement(StockOrder anOrder) -> void addElement(StockOrder anOrder);


                // same as above role, but more explicit syntax (just for illustration):
                void persistNewElement(Object entity) <- after void addElement(StockOrder anOrder);

                void removeElement(Object entity) <- before void removeElement(StockOrder anOrder);
        }
}