001//
002// Name
003//  $RCSfile: Extent.java,v $
004//
005// Copyright
006//  Copyright 2014 Cloud Software Group, Inc. ALL RIGHTS RESERVED.
007//  Cloud Software Group, Inc. Confidential Information
008//
009// History
010//  $Revision: 1.1.2.7 $ $Date: 2014/11/26 22:22:11 $
011//
012package com.kabira.store;
013
014import com.kabira.platform.annotation.Managed;
015import com.kabira.platform.LockMode;
016import com.kabira.platform.ManagedClassError;
017import com.kabira.platform.ManagedObject;
018import com.kabira.platform.Transaction;
019
020import java.util.HashSet;
021
022/**
023 * Class for receiving notifications of
024 * {@link com.kabira.platform.ManagedObject#extent} calls.
025 * <p>
026 * An <code>Extent</code> notifier does not directly return results.
027 * The notification provides an opportunity to change the contents of
028 * a Managed object extent (creating or deleting Managed objects)
029 * before the extent iteration is done.
030 * For example, it could be used for creating Managed objects from data
031 * in a database.
032 *
033 * @param <T>   Managed type parameter
034 */
035@Managed
036public abstract class Extent<T> extends ExtentBase
037{
038    // force usage of protected ctor
039    private Extent()
040    {
041    }
042
043    /** Constructor.
044     * <p>
045     * Creating an instance automatically installs it
046     * as the extent notifier for Managed class <code>T</code>.
047     * <p>
048     * If a notifier is already installed for the class,
049     * the new instance replaces the previous instance.
050     * <p>
051     * Deleting a notifier automatically uninstalls it.
052     * <p>
053     * Notifiers are automatically deleted when the JVM shuts down.
054     * Notifiers created in other JVMs are not affected.
055     * <p>
056     * A separate notifier may be set for each class in a hierarchy.
057     * <p>
058     * If no notifier is installed for a class,
059     * it inherits the notifier of its parent.
060     * <p>
061     * When a {@link ManagedObject#extent} result set is iterated,
062     * the installed notifier for its type and subtypes are called
063     * before executing the extent call.
064     *
065     * The notifiers are called starting at the most extended
066     * subtype up through the type hierarchy ending on the class
067     * passed to the extent call.
068     * The order of notifier invocation for multiple types at the
069     * same level of the type hierarchy is undefined.
070     *
071     * @param klass The Managed class base to receive
072     *              ManagedObject.extent() notifications for.
073     * @throws ManagedClassError <code>klass</code> is not managed.
074     */
075    protected Extent(final Class<T> klass)
076        throws ManagedClassError
077    {
078        audit(klass);
079
080        m_className = klass.getName();
081        install();
082        NotifierHash.addToHash(this);
083    }
084
085    private static void audit(final Class<?> klass)
086    {
087        if (! ManagedObject.isManagedClass(klass))
088        {
089            throw new ManagedClassError(
090                    klass.getName() + " is not @Managed");
091        }
092    }
093
094    /**
095     * Return the currently installed notifier for a Managed class.
096     * Inheritance is not considered.
097     * <p>
098     * If there is no notifier for the Managed class null is returned.
099     *
100     * @param klass The class to search.
101     * @return Extent or null.
102     * @throws com.kabira.platform.ManagedClassError If
103     *         <code>klass</code> is not managed.
104     */
105    public static Extent<?> getNotifier(final Class<?> klass)
106    {
107        audit(klass);
108        return (Extent)StoreUtil.getExtentNotifier(klass.getName());
109    }
110
111    /**
112     * Notifier method for
113     * {@link com.kabira.platform.ManagedObject#extent(Class)} calls.
114     * May be used to create (or delete) objects in the extent before
115     * the extent is iterated.
116     * <p>
117     * Called by the system when a {@link ManagedObject#extent} result set is
118     * iterated, before executing the extent call.
119     * <p>
120     * Called on each of the nodes in the
121     * {@link com.kabira.platform.QueryScope} of the extent call.
122     * <p>
123     * Called before the ManagedObject.extent() is performed.
124     * <p>
125     * Work done within Extent.extent() will not
126     * generate calls to com.kabira.store notifiers on the local node.
127     * <p>
128     * <b>Note:</b> work done within Extent.extent() will generate
129     * calls to com.kabira.store notifiers on remote nodes.
130     *
131     * @param klass     The Managed class for the extent() call.
132     *                  This may be a subtype of the notifier's managed
133     *                  class type.
134     *
135     * @param lockMode The lock mode for the extent call. The notifier
136     *                 implementation should attempt to honor this lock mode,
137     *                 but there is no enforcement.
138     */
139    public abstract void extent(
140            final Class<? extends T> klass,
141            final LockMode lockMode);
142
143    private void extentInternal(
144            final Class<? extends T> klass,
145            final int lockMode)
146    {
147        extent(klass, LockMode.values()[lockMode]);
148    }
149
150    static
151    {
152        Runtime.getRuntime().addShutdownHook(new Thread()
153        {
154            @Override
155            public void run()
156            {
157                new Transaction("Extent notifier shutdown")
158                {
159                    @Override
160                    protected void run()
161                    {
162                        NotifierHash.clearNotifiers();
163                    }
164                }.execute();
165            }
166        });
167    }
168
169    private static class NotifierHash
170    {
171        static final HashSet<Extent<?>> m_installedNotifiers
172            = new HashSet<Extent<?>>();
173
174        static void addToHash(final Extent<?> notifier)
175        {
176            m_installedNotifiers.add(notifier);
177        }
178
179        static void clearNotifiers()
180        {
181            for (Extent<?> extent : m_installedNotifiers)
182            {
183                if (! ManagedObject.isEmpty(extent))
184                {
185                    ManagedObject.delete(extent);
186                }
187            }
188        }
189    }
190}