Atomic create or select

The ability to atomically either select or create a uniquely keyed object (see the section called “Atomic Create or Select” for more details) is supported in the secondary store notifiers by a series of notifier calls depending on whether the object exists in shared memory after the Query<T>.query method is called. If the object exists, no other notifier entry points are called. If the object does not exist, the object is created in shared memory by the TIBCO ActiveSpaces® Transactions runtime and then the Record<T>.created notifier is called.

This behavior is shown in Example 10.6, “Atomic create or select”.

Example 10.6. Atomic create or select

//     $Revision: 1.1.2.2 $
package com.kabira.snippets.store;

import com.kabira.platform.KeyFieldValueList;
import com.kabira.platform.KeyManager;
import com.kabira.platform.KeyQuery;
import com.kabira.platform.LockMode;
import com.kabira.platform.Transaction;
import com.kabira.platform.annotation.Key;
import com.kabira.platform.annotation.Managed;
import com.kabira.store.KeyField;
import com.kabira.store.Query;
import com.kabira.store.Record;
import java.util.ArrayList;

/**
 * Secondary store notifier behavior with an atomic select or create query
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainnode</b> = A
 * </ul>
 */
public class AtomicCreateSelect
{
    @Managed
    @Key(name = "ByName", unique = true, ordered = true, fields =
    {
        "m_name"
    })
    private static class A
    {
        private A(final String name)
        {
            m_name = name;
        }
        
        String getName()
        {
            return m_name;
        }

        private final String m_name;
    };
    
    private static class RecordNotifier extends Record<A>
    {
        public RecordNotifier()
        {
            super(A.class);
        }

        @Override
        public void created(A a)
        {
            System.out.println("INFO:\tCreated " + a.getName());
        }
    }
    
    private static class QueryNotifier extends Query<A>
    {
        public QueryNotifier()
        {
            super(A.class, "ByName");
        }

        @Override
        public void query(Class<? extends A> klass, LockMode lockMode, ArrayList<KeyField> queryData)
        {
            assert queryData.size() == 1 : queryData.size();
            assert queryData.get(0).name.equals("m_name") : queryData.get(0);
            assert queryData.get(0).value instanceof String : queryData.get(0);
            String value = (String) queryData.get(0).value;
            
            if (m_create == true)
            {
                System.out.println("INFO:\tQuery creating object for " + value);
                new A(value);
                m_create = false;
            }
            else
            {
                System.out.println("INFO:\tQuery NOT creating object for " + value);                
            }
        }

        @Override
        public void queryMinimum(Class<? extends A> klass, LockMode lm, ArrayList<KeyField> al)
        {
            assert false : "queryMinimum";
        }

        @Override
        public void queryMaximum(Class<? extends A> klass, LockMode lm, ArrayList<KeyField> al)
        {
            assert false : "queryMaximum";
        }
        
        private boolean m_create = true;
    }
    
    /**
     * Main entry point
     * @param args Not used
     */
    public static void main (final String [ ] args)
    {
        new Transaction("Initialize")
        {
            
            @Override
            protected void run()
            {
                new RecordNotifier();
                new QueryNotifier();
            }
        }.execute();
        
        new Transaction("Create or Select")
        {
            @Override
            protected void run()
            {
                KeyManager<A> km =  new KeyManager<>();
                KeyQuery<A> kq = km.createKeyQuery(A.class, "ByName");
                KeyFieldValueList values = new KeyFieldValueList();
                
                values.add("m_name", "foo");
                kq.defineQuery(values);
                
                System.out.println("INFO: " + kq);
                A a = kq.getOrCreateSingleResult(LockMode.WRITELOCK, null);
                System.out.println("INFO: Returned " + a.getName());
                
                values.clear();
                values.add("m_name", "bar");
                kq.defineQuery(values);
                
                System.out.println("INFO: " + kq);
                a = kq.getOrCreateSingleResult(LockMode.WRITELOCK, null);
                System.out.println("INFO: Returned " + a.getName());
            }
        }.execute();
    }
}

Example 10.6, “Atomic create or select” outputs these message when run (annotation added):

#
#  Query<T>.query creates object in shared memory
#
INFO: select obj from com.kabira.snippets.store.AtomicCreateSelect$A using ByName where (m_name == foo); Java constructor: (none)
INFO:    Query creating object for foo
INFO: Returned foo

#
#  Query<T>.query does not create object in shared memory
#  Object is created by the runtime and the Record<T>created
#  notifier is called
#
INFO: select obj from com.kabira.snippets.store.AtomicCreateSelect$A using ByName where (m_name == bar); Java constructor: (none)
INFO:    Query NOT creating object for bar
INFO: Returned bar
INFO:    Created bar