001//
002// Name
003//  $RCSfile: Record.java,v $
004//
005// Copyright
006//  Copyright 2014-2015 Cloud Software Group, Inc. ALL RIGHTS RESERVED.
007//  Cloud Software Group, Inc. Confidential Information
008//
009// History
010//  $Revision: 1.1.2.7 $ $Date: 2015/01/29 01:48:38 $
011//
012package com.kabira.store;
013
014import com.kabira.platform.*;
015import com.kabira.platform.annotation.Managed;
016
017import java.util.HashSet;
018
019/**
020 * Class for receiving notifications of
021 * modifications (create, update, delete)
022 * to Managed object instances.
023 * <p>
024 * This notifier provides an opportunity to see Managed
025 * objects modified within a transaction.
026 * For example, it could be used for updating records in a database, or
027 * for doing a Managed object change log.
028 *
029 * @param <T>   Managed type parameter
030 */
031@Managed
032public abstract class Record<T> extends RecordBase
033{
034    // This must map to the notifier::Modification modeled enum values.
035    private static final int BaseNotifier_Created = 1;
036    private static final int BaseNotifier_Deleted = 2;
037    private static final int BaseNotifier_Modified = 3;
038
039    // force usage of protected ctor
040    private Record()
041    {
042    }
043
044    /** Constructor.
045     * <p>
046     * Creating an instance automatically installs it
047     * as the record notifier for Managed class <code>T</code>.
048     * <p>
049     * If a notifier is already installed for the class,
050     * the new instance replaces the previous instance.
051     * <p>
052     * Deleting a notifier automatically uninstalls it.
053     * <p>
054     * Notifiers are automatically deleted when the JVM shuts down.
055     * Notifiers created in other JVMs are not affected.
056     * <p>
057     * A separate notifier may be set for each class in a hierarchy.
058     * <p>
059     * If no notifier is installed for a class,
060     * it inherits the notifier of its parent.
061     *
062     * @param klass The Managed class base to receive notifications for.
063     * @throws ManagedClassError <code>klass</code> is not managed.
064     */
065    protected Record(final Class<T> klass) throws ManagedClassError
066    {
067        audit(klass);
068
069        m_className = klass.getName();
070        install();
071        NotifierHash.addToHash(this);
072    }
073
074    private static void audit(final Class<?> klass)
075    {
076        if (!ManagedObject.isManagedClass(klass))
077        {
078            throw new ManagedClassError(klass.getName() + " is not @Managed");
079        }
080    }
081
082    /**
083     * Return the currently installed Record notifier for
084     * a Managed class.
085     * <b>Note:</b>
086     * Inheritance is not examined by this method.
087     * If there is no notifier for the Managed class
088     * null is returned.
089     *
090     * @param klass The class to search.
091     * @return Record or null.
092     * @throws com.kabira.platform.ManagedClassError if
093     *         <code>klass</code> is not managed.
094     */
095    public static Record<?> getNotifier(final Class<?> klass)
096    {
097        audit(klass);
098        return (Record)StoreUtil.getRecordNotifier(klass.getName());
099    }
100
101    /**
102     * Notifier method for a created Managed object.
103     * <p>
104     * Called for each object created (but not deleted) within the transaction.
105     * @param object The created object.
106     *                  This may be a subtype of the notifier's managed
107     *                  class type.
108     */
109    public void created(final T object)
110    {
111    }
112
113    /**
114     * Notifier method for a modified Managed object.
115     * <p>
116     * Called for each object modified
117     * (but not created or deleted) within the transaction.
118     * <p>
119     * @param object The modified object.
120     *                  This may be a subtype of the notifier's managed
121     *                  class type.
122     */
123    public void modified(final T object)
124    {
125    }
126
127    /**
128     * Notifier method for a deleted Managed object.
129     * <p>
130     * Called when {@link ManagedObject#delete} is called by the application.
131     * <p>
132     * @param object The deleted object.
133     *                  This may be a subtype of the notifier's managed
134     *                  class type.
135     */
136    public void deleted(final T object)
137    {
138    }
139
140    private void modifiedInternal(
141            final T object, final int baseNotifierModification)
142    {
143        if (baseNotifierModification == BaseNotifier_Modified)
144        {
145            modified(object);
146        }
147        else if (baseNotifierModification == BaseNotifier_Created)
148        {
149            created(object);
150        }
151        else
152        {
153            assert baseNotifierModification == BaseNotifier_Deleted
154                : baseNotifierModification + " != BaseNotifier.Deleted";
155            deleted(object);
156        }
157    }
158
159    static
160    {
161        Runtime.getRuntime().addShutdownHook(new Thread()
162        {
163            @Override
164            public void run()
165            {
166                new Transaction("ObjectModified notifier shutdown")
167                {
168                    @Override
169                    protected void run()
170                    {
171                        NotifierHash.clearNotifiers();
172                    }
173                }.execute();
174            }
175        });
176    }
177
178    private static class NotifierHash
179    {
180        static final HashSet<Record<?>> m_installedNotifiers
181                = new HashSet<Record<?>>();
182
183        static void addToHash(final Record<?> notifier)
184        {
185            m_installedNotifiers.add(notifier);
186        }
187
188        static void clearNotifiers()
189        {
190            for (Record<?> record : m_installedNotifiers)
191            {
192                if (! ManagedObject.isEmpty(record))
193                {
194                    ManagedObject.delete(record);
195                }
196            }
197        }
198    }
199}