001 package org.cumulus4j.store.model; 002 003 import java.util.ArrayList; 004 import java.util.Collection; 005 import java.util.HashMap; 006 import java.util.Map; 007 import java.util.concurrent.atomic.AtomicLong; 008 009 import javax.jdo.annotations.Column; 010 import javax.jdo.annotations.Discriminator; 011 import javax.jdo.annotations.DiscriminatorStrategy; 012 import javax.jdo.annotations.IdentityType; 013 import javax.jdo.annotations.Inheritance; 014 import javax.jdo.annotations.InheritanceStrategy; 015 import javax.jdo.annotations.NotPersistent; 016 import javax.jdo.annotations.PersistenceCapable; 017 import javax.jdo.annotations.Queries; 018 import javax.jdo.annotations.Query; 019 import javax.jdo.annotations.Unique; 020 import javax.jdo.annotations.Uniques; 021 022 import org.datanucleus.store.ExecutionContext; 023 024 @PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true") 025 @Inheritance(strategy=InheritanceStrategy.NEW_TABLE) 026 @Discriminator(strategy=DiscriminatorStrategy.VALUE_MAP, value="EmbeddedClassMeta") 027 @Uniques({ 028 @Unique(members={"embeddingFieldMeta_fieldID"}, columns=@Column(name="discriminator")) 029 }) 030 @Queries({ 031 @Query( 032 name=EmbeddedClassMeta.NamedQueries.getEmbeddedClassMetaByEmbeddingFieldMeta_fieldID, 033 value="SELECT UNIQUE FROM org.cumulus4j.store.model.EmbeddedClassMeta EXCLUDE SUBCLASSES WHERE this.embeddingFieldMeta_fieldID == :embeddingFieldMeta_fieldID" 034 ) 035 }) 036 public class EmbeddedClassMeta extends ClassMeta { 037 038 protected static final String UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META = EmbeddedClassMeta.class.getSimpleName() + '.'; 039 040 protected static class NamedQueries { 041 public static final String getEmbeddedClassMetaByEmbeddingFieldMeta_fieldID = "getEmbeddedClassMetaByEmbeddingFieldMeta_fieldID"; 042 } 043 044 // @Persistent(nullValue=NullValue.EXCEPTION) 045 @NotPersistent 046 private ClassMeta nonEmbeddedClassMeta; 047 048 private long nonEmbeddedClassMeta_classID; 049 050 // @Persistent(nullValue=NullValue.EXCEPTION) 051 @NotPersistent 052 private FieldMeta embeddingFieldMeta; 053 054 private long embeddingFieldMeta_fieldID; 055 056 @NotPersistent 057 private Map<FieldMeta, EmbeddedFieldMeta> nonEmbeddedFieldMeta2EmbeddedFieldMeta; 058 059 protected EmbeddedClassMeta() { } 060 061 public EmbeddedClassMeta(ExecutionContext executionContext, ClassMeta nonEmbeddedClassMeta, FieldMeta embeddingFieldMeta) { 062 super(executionContext.getClassLoaderResolver().classForName(nonEmbeddedClassMeta.getClassName())); 063 if (embeddingFieldMeta == null) 064 throw new IllegalArgumentException("embeddingFieldMeta == null"); 065 066 if (nonEmbeddedClassMeta instanceof EmbeddedClassMeta) 067 // nonEmbeddedClassMeta = ((EmbeddedClassMeta) nonEmbeddedClassMeta).getNonEmbeddedClassMeta(); 068 throw new IllegalArgumentException("nonEmbeddedClassMeta is an instance of EmbeddedClassMeta: " + nonEmbeddedClassMeta); 069 070 this.nonEmbeddedClassMeta = nonEmbeddedClassMeta; 071 this.nonEmbeddedClassMeta_classID = nonEmbeddedClassMeta.getClassID(); 072 if (nonEmbeddedClassMeta_classID < 0) 073 throw new IllegalStateException("nonEmbeddedClassMeta not yet persisted: " + nonEmbeddedClassMeta); 074 075 this.embeddingFieldMeta = embeddingFieldMeta; 076 this.embeddingFieldMeta_fieldID = embeddingFieldMeta.getFieldID(); 077 if (embeddingFieldMeta_fieldID < 0) 078 throw new IllegalStateException("embeddingFieldMeta not yet persisted: " + embeddingFieldMeta); 079 080 // setUniqueScope(null); // set in jdoPreStore, because id not assigned, yet 081 setUniqueScope(UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META + embeddingFieldMeta_fieldID); 082 } 083 084 @Override 085 public void addFieldMeta(FieldMeta fieldMeta) { 086 if (!(fieldMeta instanceof EmbeddedFieldMeta)) { 087 throw new IllegalArgumentException("fieldMeta is NOT an instance of EmbeddedFieldMeta: " + fieldMeta); 088 } 089 super.addFieldMeta(fieldMeta); 090 Map<FieldMeta, EmbeddedFieldMeta> nefm2efmMap = nonEmbeddedFieldMeta2EmbeddedFieldMeta; 091 if (nefm2efmMap != null) { 092 EmbeddedFieldMeta embeddedFieldMeta = (EmbeddedFieldMeta) fieldMeta; 093 nefm2efmMap.put(embeddedFieldMeta.getNonEmbeddedFieldMeta(), embeddedFieldMeta); 094 } 095 } 096 097 @Override 098 public void removeFieldMeta(FieldMeta fieldMeta) { 099 super.removeFieldMeta(fieldMeta); 100 nonEmbeddedFieldMeta2EmbeddedFieldMeta = null; 101 } 102 103 /** 104 * Get the non-embedded {@link ClassMeta} of which this instance is a reference wihtin the scope of 105 * the {@link #getEmbeddingFieldMeta()}. 106 * @return the non-embedded {@link ClassMeta} (the one representing FCOs). Never <code>null</code>. 107 */ 108 public ClassMeta getNonEmbeddedClassMeta() { 109 if (nonEmbeddedClassMeta == null) { 110 nonEmbeddedClassMeta = new ClassMetaDAO(getPersistenceManager()).getClassMeta(nonEmbeddedClassMeta_classID, true); 111 } 112 return nonEmbeddedClassMeta; 113 } 114 115 /** 116 * Get the field embedding this pseudo-class. 117 * <p> 118 * This may be an {@link EmbeddedFieldMeta}, if this is a nested-embedded-field-situation. 119 * @return the field embedding this pseudo-class. Never <code>null</code>. 120 */ 121 public FieldMeta getEmbeddingFieldMeta() { 122 if (embeddingFieldMeta == null) { 123 embeddingFieldMeta = new FieldMetaDAO(getPersistenceManager()).getFieldMeta(embeddingFieldMeta_fieldID, true); 124 } 125 return embeddingFieldMeta; 126 } 127 128 // @Override 129 // public void jdoPreStore() { 130 // super.jdoPreStore(); 131 // if (getUniqueScope() == null || !getUniqueScope().startsWith(UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META)) { 132 // setUniqueScope("TEMPORARY_" + UUID.randomUUID()); 133 // if (nonEmbeddedClassMeta_classID < 0) 134 // nonEmbeddedClassMeta_classID = nextTemp_nonEmbeddedClassMeta_classID.decrementAndGet(); 135 // 136 // if (embeddingFieldMeta_fieldID < 0) 137 // embeddingFieldMeta_fieldID = nextTemp_embeddingFieldMeta_fieldID.decrementAndGet(); 138 // 139 // PostStoreRunnableManager.getInstance().addRunnable(new Runnable() { 140 // @Override 141 // public void run() { 142 // PersistenceManager pm = JDOHelper.getPersistenceManager(EmbeddedClassMeta.this); 143 // 144 // if (nonEmbeddedClassMeta != null) 145 // nonEmbeddedClassMeta = pm.makePersistent(nonEmbeddedClassMeta); 146 // 147 // if (nonEmbeddedClassMeta_classID < 0 && nonEmbeddedClassMeta != null) 148 // nonEmbeddedClassMeta_classID = nonEmbeddedClassMeta.getClassID(); 149 // 150 // if (nonEmbeddedClassMeta_classID < 0) 151 // throw new IllegalStateException("nonEmbeddedClassMeta_classID < 0"); 152 // 153 // if (embeddingFieldMeta != null) 154 // embeddingFieldMeta = pm.makePersistent(embeddingFieldMeta); 155 // 156 // if (embeddingFieldMeta_fieldID < 0 && embeddingFieldMeta != null) 157 // embeddingFieldMeta_fieldID = embeddingFieldMeta.getFieldID(); 158 // 159 // if (embeddingFieldMeta_fieldID < 0) 160 // throw new IllegalStateException("embeddingFieldMeta_fieldID < 0"); 161 // 162 // setUniqueScope(UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META + embeddingFieldMeta.getFieldID()); 163 // 164 // pm.flush(); 165 // } 166 // }); 167 // } 168 // } 169 170 private static AtomicLong nextTemp_nonEmbeddedClassMeta_classID = new AtomicLong(); 171 private static AtomicLong nextTemp_embeddingFieldMeta_fieldID = new AtomicLong(); 172 173 /** 174 * Get the {@link FieldMeta} managed by this instances corresponding to the given <code>fieldMeta</code>. 175 * <p> 176 * The given <code>fieldMeta</code> can be a sub-FieldMeta (not directly assigned to the corresponding ClassMeta, 177 * but assigned to one of its FieldMetas). 178 * @param fieldMeta a non-embedded {@link FieldMeta} (i.e. <b>not</b> an instance of {@link EmbeddedFieldMeta}). 179 * @return 180 */ 181 public EmbeddedFieldMeta getEmbeddedFieldMetaForNonEmbeddedFieldMeta(FieldMeta fieldMeta) { 182 if (fieldMeta == null) 183 throw new IllegalArgumentException("fieldMeta == null"); 184 185 if (fieldMeta instanceof EmbeddedFieldMeta) 186 throw new IllegalArgumentException("fieldMeta is an instance of EmbeddedFieldMeta, but it should be a non-embedded FieldMeta!"); 187 188 if (!this.getNonEmbeddedClassMeta().equals(fieldMeta.getClassMeta())) 189 throw new IllegalArgumentException("fieldMeta.classMeta != this.nonEmbeddedClassMeta"); 190 191 if (nonEmbeddedFieldMeta2EmbeddedFieldMeta == null) { 192 Map<FieldMeta, EmbeddedFieldMeta> m = new HashMap<FieldMeta, EmbeddedFieldMeta>(); 193 for (FieldMeta efm : getFieldMetasWithSubFieldMetas()) { 194 EmbeddedFieldMeta embeddedFieldMeta = (EmbeddedFieldMeta) efm; 195 m.put(embeddedFieldMeta.getNonEmbeddedFieldMeta(), embeddedFieldMeta); 196 } 197 nonEmbeddedFieldMeta2EmbeddedFieldMeta = m; 198 } 199 return nonEmbeddedFieldMeta2EmbeddedFieldMeta.get(fieldMeta); 200 } 201 202 protected Collection<FieldMeta> getFieldMetasWithSubFieldMetas() { 203 Collection<FieldMeta> result = new ArrayList<FieldMeta>(); 204 for (FieldMeta fieldMeta : getFieldMetas()) { 205 populateFieldMetasWithSubFieldMetas(result, fieldMeta); 206 } 207 return result; 208 } 209 protected void populateFieldMetasWithSubFieldMetas(Collection<FieldMeta> result, FieldMeta fieldMeta) { 210 result.add(fieldMeta); 211 for (FieldMeta subFieldMeta : fieldMeta.getSubFieldMetas()) { 212 populateFieldMetasWithSubFieldMetas(result, subFieldMeta); 213 } 214 } 215 216 @Override 217 public void jdoPostDetach(Object o) { 218 final PostDetachRunnableManager postDetachRunnableManager = PostDetachRunnableManager.getInstance(); 219 postDetachRunnableManager.enterScope(); 220 try { 221 super.jdoPostDetach(o); 222 postDetachRunnableManager.addRunnable(new Runnable() { 223 @Override 224 public void run() { 225 DetachedClassMetaModel detachedClassMetaModel = DetachedClassMetaModel.getInstance(); 226 227 if (detachedClassMetaModel == null) // we currently only detach with this being present - at least it should, hence we don't need to handle things differently. 228 throw new IllegalStateException("DetachedClassMetaModel.getInstance() returned null!"); 229 230 if (detachedClassMetaModel != null) { 231 nonEmbeddedClassMeta = detachedClassMetaModel.getClassMeta(nonEmbeddedClassMeta_classID, false); 232 if (nonEmbeddedClassMeta == null) 233 setNonEmbeddedClassMetaPostponedInPostDetach(postDetachRunnableManager, detachedClassMetaModel, 1); 234 } 235 } 236 }); 237 } finally { 238 postDetachRunnableManager.exitScope(); 239 } 240 } 241 242 protected void setNonEmbeddedClassMetaPostponedInPostDetach(final PostDetachRunnableManager postDetachRunnableManager, final DetachedClassMetaModel detachedClassMetaModel, final int postponeCounter) { 243 postDetachRunnableManager.addRunnable(new Runnable() { 244 @Override 245 public void run() { 246 nonEmbeddedClassMeta = detachedClassMetaModel.getClassMeta(nonEmbeddedClassMeta_classID, false); 247 if (nonEmbeddedClassMeta == null) { 248 final int maxPostponeCounter = 100; 249 if (postponeCounter > maxPostponeCounter) 250 throw new IllegalStateException("postponeCounter > " + maxPostponeCounter); 251 252 setNonEmbeddedClassMetaPostponedInPostDetach(postDetachRunnableManager, detachedClassMetaModel, postponeCounter + 1); 253 } 254 } 255 }); 256 } 257 }