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}