1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.enhance;
20
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.LinkedList;
25 import java.util.Map;
26
27 import org.apache.openjpa.lib.util.Localizer;
28 import org.apache.openjpa.lib.util.ReferenceMap;
29 import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap;
30 import org.apache.openjpa.util.UserException;
31 import org.apache.openjpa.util.InvalidStateException;
32
33 /**
34 * Tracks registered persistence-capable classes.
35 *
36 * @since 0.4.0
37 * @author Abe White
38 */
39 public class PCRegistry {
40 // DO NOT ADD ADDITIONAL DEPENDENCIES TO THIS CLASS
41
42 private static final Localizer _loc = Localizer.forPackage
43 (PCRegistry.class);
44
45 // map of pc classes to meta structs; weak so the VM can GC classes
46 private static final Map _metas = new ConcurrentReferenceHashMap
47 (ReferenceMap.WEAK, ReferenceMap.HARD);
48
49 // register class listeners
50 private static final Collection _listeners = new LinkedList();
51
52 /**
53 * Register a {@link RegisterClassListener}.
54 */
55 public static void addRegisterClassListener(RegisterClassListener rcl) {
56 if (rcl == null)
57 return;
58
59 // we have to be positive that every listener gets notified for
60 // every class, so lots of locking
61 synchronized (_listeners) {
62 _listeners.add(rcl);
63 }
64 synchronized (_metas) {
65 for (Iterator itr = _metas.keySet().iterator(); itr.hasNext();)
66 rcl.register((Class) itr.next());
67 }
68 }
69
70 /**
71 * Removes a {@link RegisterClassListener}.
72 */
73 public static void removeRegisterClassListener(RegisterClassListener rcl) {
74 synchronized (_listeners) {
75 _listeners.remove(rcl);
76 }
77 }
78
79 /**
80 * Get the field names for a <code>PersistenceCapable</code> class.
81 */
82 public static String[] getFieldNames(Class pcClass) {
83 Meta meta = getMeta(pcClass);
84 return meta.fieldNames;
85 }
86
87 /**
88 * Get the field types for a <code>PersistenceCapable</code> class.
89 */
90 public static Class[] getFieldTypes(Class pcClass) {
91 Meta meta = getMeta(pcClass);
92 return meta.fieldTypes;
93 }
94
95 /**
96 * Return the persistent superclass for a <code>PersistenceCapable</code>
97 * class, or null if none. The superclass may or may not implement
98 * {@link PersistenceCapable}, depending on the access type of the class.
99 */
100 public static Class getPersistentSuperclass(Class pcClass) {
101 Meta meta = getMeta(pcClass);
102 return meta.pcSuper;
103 }
104
105 /**
106 * Create a new instance of the class and assign its state manager.
107 * The new instance has its flags set to <code>LOAD_REQUIRED</code>.
108 */
109 public static PersistenceCapable newInstance(Class pcClass,
110 StateManager sm, boolean clear) {
111 Meta meta = getMeta(pcClass);
112 return (meta.pc == null) ? null : meta.pc.pcNewInstance(sm, clear);
113 }
114
115 /**
116 * Create a new instance of the class and assign its state manager and oid.
117 * The new instance has its flags set to <code>LOAD_REQUIRED</code>.
118 */
119 public static PersistenceCapable newInstance(Class pcClass,
120 StateManager sm, Object oid, boolean clear) {
121 Meta meta = getMeta(pcClass);
122 return (meta.pc == null) ? null : meta.pc.pcNewInstance(sm, oid, clear);
123 }
124
125 /**
126 * Return the persistence-capable type for <code>type</code>. This might
127 * be a generated subclass of <code>type</code>.
128 *
129 * @since 1.1.0
130 */
131 public static Class getPCType(Class type) {
132 Meta meta = getMeta(type);
133 return (meta.pc == null) ? null : meta.pc.getClass();
134 }
135
136 /**
137 * Create a new identity object for the given
138 * <code>PersistenceCapable</code> class.
139 */
140 public static Object newObjectId(Class pcClass) {
141 Meta meta = getMeta(pcClass);
142 return (meta.pc == null) ? null : meta.pc.pcNewObjectIdInstance();
143 }
144
145 /**
146 * Create a new identity object for the given
147 * <code>PersistenceCapable</code> class, using the <code>String</code>
148 * form of the constructor.
149 */
150 public static Object newObjectId(Class pcClass, String str) {
151 Meta meta = getMeta(pcClass);
152 return (meta.pc == null) ? null : meta.pc.pcNewObjectIdInstance(str);
153 }
154
155 /**
156 * Return the alias for the given type.
157 */
158 public static String getTypeAlias(Class pcClass) {
159 return getMeta(pcClass).alias;
160 }
161
162 /**
163 * Copy fields from an outside source to the key fields in the identity
164 * object.
165 */
166 public static void copyKeyFieldsToObjectId(Class pcClass, FieldSupplier fm,
167 Object oid) {
168 Meta meta = getMeta(pcClass);
169 if (meta.pc == null)
170 throw new UserException(_loc.get("copy-no-id", pcClass));
171
172 meta.pc.pcCopyKeyFieldsToObjectId(fm, oid);
173 }
174
175 /**
176 * Copy fields to an outside source from the key fields in the identity
177 * object.
178 */
179 public static void copyKeyFieldsFromObjectId(Class pcClass,
180 FieldConsumer fm, Object oid) {
181 Meta meta = getMeta(pcClass);
182 if (meta.pc == null)
183 throw new UserException(_loc.get("copy-no-id", pcClass));
184
185 meta.pc.pcCopyKeyFieldsFromObjectId(fm, oid);
186 }
187
188 /**
189 * Register metadata by class.
190 *
191 * @param fieldTypes managed field types
192 * @param fieldFlags managed field flags
193 * @param sup the most immediate persistent superclass
194 * @param pcClass the <code>PersistenceCapable</code> class
195 * @param fieldNames managed field names
196 * @param alias the class alias
197 * @param pc an instance of the class, if not abstract
198 */
199 public static void register(Class pcClass, String[] fieldNames,
200 Class[] fieldTypes, byte[] fieldFlags, Class sup, String alias,
201 PersistenceCapable pc) {
202 if (pcClass == null)
203 throw new NullPointerException();
204
205 // we have to be positive that every listener gets notified for
206 // every class, so lots of locking
207 Meta meta = new Meta(pc, fieldNames, fieldTypes, sup, alias);
208 synchronized (_metas) {
209 _metas.put(pcClass, meta);
210 }
211 synchronized (_listeners) {
212 for (Iterator i = _listeners.iterator(); i.hasNext();)
213 ((RegisterClassListener) i.next()).register(pcClass);
214 }
215 }
216
217 /**
218 * De-Register all metadata associated with the given ClassLoader.
219 * Allows ClassLoaders to be garbage collected.
220 *
221 * @param cl the ClassLoader
222 */
223 public static void deRegister(ClassLoader cl) {
224 synchronized (_metas) {
225 for (Iterator i = _metas.keySet().iterator(); i.hasNext();) {
226 Class pcClass = (Class) i.next();
227 if (pcClass.getClassLoader() == cl) {
228 _metas.remove(pcClass);
229 }
230 }
231 }
232 }
233
234 /**
235 * Returns a collection of class objects of the registered
236 * persistence-capable classes.
237 */
238 public static Collection getRegisteredTypes() {
239 return Collections.unmodifiableCollection(_metas.keySet());
240 }
241
242 /**
243 * Returns <code>true</code> if <code>cls</code> is already registered.
244 */
245 public static boolean isRegistered(Class cls) {
246 return _metas.containsKey(cls);
247 }
248
249 /**
250 * Look up the metadata for a <code>PersistenceCapable</code> class.
251 */
252 private static Meta getMeta(Class pcClass) {
253 Meta ret = (Meta) _metas.get(pcClass);
254 if (ret == null)
255 throw new IllegalStateException(_loc.get("no-meta", pcClass).
256 getMessage());
257 return ret;
258 }
259
260 /**
261 * Listener for persistent class registration events.
262 */
263 public static interface RegisterClassListener {
264
265 public void register(Class cls);
266 }
267
268 /**
269 * This is a helper class to manage metadata per persistence-capable class.
270 */
271 private static class Meta {
272
273 public final PersistenceCapable pc;
274 public final String[] fieldNames;
275 public final Class[] fieldTypes;
276 public final Class pcSuper;
277 public final String alias;
278
279 public Meta(PersistenceCapable pc, String[] fieldNames,
280 Class[] fieldTypes, Class pcSuper, String alias) {
281 this.pc = pc;
282 this.fieldNames = fieldNames;
283 this.fieldTypes = fieldTypes;
284 this.pcSuper = pcSuper;
285 this.alias = alias;
286 }
287 }
288 }