001: /**
002: * Copyright (C) 2001-2004 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.speedo.mim.lib;
018:
019: import org.objectweb.jorm.api.PClassMapping;
020: import org.objectweb.jorm.api.PException;
021: import org.objectweb.perseus.persistence.api.PersistenceException;
022: import org.objectweb.perseus.persistence.api.RolledBackPersistenceException;
023: import org.objectweb.perseus.persistence.api.TransactionalPersistenceManager;
024: import org.objectweb.speedo.api.Debug;
025: import org.objectweb.speedo.api.ExceptionHelper;
026: import org.objectweb.speedo.lib.Personality;
027: import org.objectweb.speedo.metadata.SpeedoFetchGroup;
028: import org.objectweb.speedo.mim.api.DetachedLifeCycle;
029: import org.objectweb.speedo.mim.api.HomeItf;
030: import org.objectweb.speedo.mim.api.StateItf;
031: import org.objectweb.speedo.mim.api.PersistentObjectItf;
032: import org.objectweb.speedo.pm.api.POManagerItf;
033: import org.objectweb.speedo.pm.api.POManagerFactoryItf;
034: import org.objectweb.speedo.query.api.QueryDefinition;
035: import org.objectweb.speedo.usercache.api.UserCache;
036: import org.objectweb.speedo.usercache.lib.CompositeUserCache;
037: import org.objectweb.speedo.usercache.lib.UserCacheImpl;
038: import org.objectweb.speedo.workingset.api.TransactionItf;
039: import org.objectweb.util.monolog.api.BasicLevel;
040: import org.objectweb.util.monolog.api.Logger;
041:
042: import java.util.ArrayList;
043: import java.util.Arrays;
044: import java.util.Collection;
045: import java.util.Collections;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Map;
049:
050: /**
051: * @author S.Chassande-Barrioz
052: */
053: public abstract class AbstractHomeImpl implements HomeItf {
054: protected TransactionalPersistenceManager tpm = null;
055: protected POManagerFactoryItf pmf = null;
056: byte cachePolicy = CACHED;
057: boolean isShareable = true;
058: boolean isFieldLockingLevel = false;
059: boolean prefetchOnQuery = true;
060: boolean prefetchOnExtent = true;
061: boolean prefetchOnGenClass = true;
062: protected Map namedQueries = null;
063: protected Personality personality;
064:
065: public AbstractHomeImpl(Personality p) {
066: personality = p;
067: }
068:
069: public AbstractHomeImpl(TransactionalPersistenceManager _tpm,
070: POManagerFactoryItf _pmf) {
071: this .tpm = _tpm;
072: this .pmf = _pmf;
073: }
074:
075: /**
076: * @return false because it corresponds to most of case
077: * Subclass to change the behavior
078: */
079: protected boolean isAbstract() {
080: return false;
081: }
082:
083: /**
084: * Create a new instance of a PersistentObjectItf which the clas is given in
085: * parameter. The default implementation does a
086: * simple 'clazz.newInstance()'.
087: */
088: protected PersistentObjectItf newSpeedoPOInstance(Class clazz)
089: throws Exception {
090: return (PersistentObjectItf) clazz.newInstance();
091: }
092:
093: // IMPLEMENTATION OF THE HomeItf INTERFACE //
094: //-------------------------------------------//
095:
096: public boolean isCacheable() {
097: return (cachePolicy & CACHED) != 0;
098: }
099:
100: public boolean allLoaded() {
101: return (cachePolicy & ALL) != 0;
102: }
103:
104: public boolean hasToFix() {
105: return (cachePolicy & FIXED) != 0;
106: }
107:
108: public void setCachePolicy(byte v) {
109: cachePolicy = v;
110: }
111:
112: public boolean isShareable() {
113: return isShareable;
114: }
115:
116: public void setShareable(boolean v) {
117: isShareable = v;
118: }
119:
120: public boolean isFieldLockingLevel() {
121: return isFieldLockingLevel;
122: }
123:
124: public void setFieldLockingLevel(boolean val) {
125: isFieldLockingLevel = val;
126: }
127:
128: /**
129: * This default implementation does nothing and returns null.
130: * Subclass to change the behavior
131: */
132: public Collection fgGetNestedFetchGroups(String fgName) {
133: return null;
134: }
135:
136: /**
137: * This default implementation returns always false.
138: * Subclass to change the behavior
139: */
140: public boolean fgIsDefined(String fgName) {
141: return false;
142: }
143:
144: /**
145: * This default implementation does nothing and returns null.
146: * Subclass to change the behavior
147: */
148: public Collection fgGetFieldsToLoad(String fgName) {
149: return null;
150: }
151:
152: /**
153: * @return the PersistenceManagerFactory which represents the data support
154: * inside which the po is persistent.
155: */
156: public final POManagerFactoryItf getPOManagerFactory() {
157: return pmf;
158: }
159:
160: /**
161: * It assignes a PersistenceManagerFactory
162: * @param _pmf is the PersistenceManagerFactory which represents the data
163: * support inside which the po is persistent.
164: */
165: public final void setPOManagerFactory(POManagerFactoryItf _pmf) {
166: this .pmf = _pmf;
167: }
168:
169: /**
170: * @return the TransactionalPersistenceManager which manage the
171: * concurrency, loading, ... of the po.
172: */
173: public final TransactionalPersistenceManager getTransactionalPersistenceManager() {
174: return tpm;
175: }
176:
177: /**
178: * It assignes a TransactionalPersistenceManager
179: * @param _tpm is the TransactionalPersistenceManager which manage the
180: * concurrency, loading, ... of the po.
181: */
182: public final void setTransactionalPersistenceManager(
183: TransactionalPersistenceManager _tpm) {
184: this .tpm = _tpm;
185: }
186:
187: /**
188: * Notifies the transactional persistency manager of a read intention for
189: * the given persistent instance.
190: * @param sp is the PersistentObjectItf which the read access is requested.
191: * @param fields the ids of the fields that may be accessed by the caller
192: * of this method. If the i-th bit of 'fields' is set to 1, then the i-th
193: * field of the given speedo accessor may be accessed by the caller of this
194: * method.
195: */
196: public final StateItf readIntention(PersistentObjectItf sp,
197: long[] fields) {
198: if (!sp.speedoIsActive()) {
199: StateItf sa = sp.speedoGetReferenceState();
200: if (sa == null) {
201: sp.speedoSetReferenceState(sp.speedoCreateState());
202: }
203: return sa;
204: }
205: POManagerItf pm = pmf.lookup();
206: if (pm == null) {
207: throw personality
208: .newUserRuntimeException("When a persistent object is used (read), a PersistenceManager is needed");
209: }
210: TransactionItf t = pm.getSpeedoTransaction();
211: try {
212: StateItf sa = (StateItf) tpm.readIntention(t, sp, null);
213: sa.loadFields(pm, fields);
214: return sa;
215: } catch (RolledBackPersistenceException e) {
216: throw t.rollBackOnInternalError(e);
217: } catch (PersistenceException e) {
218: throw personality.newRuntimeException(
219: "Impossible to notify a read intention",
220: ExceptionHelper.getNested(e));
221: }
222: }
223:
224: public final StateItf writeIntention(PersistentObjectItf sp,
225: long[] fields) {
226: return writeIntention(sp, fields, null);
227: }
228:
229: /**
230: * Notifies the transactional persistency manager of a write intention for
231: * the given persistent instance.
232: *
233: * @param sp is the PersistentObjectItf which the write access is requested.
234: * @param fields the ids of the fields that may be accessed by the caller
235: * of this method. If the i-th bit of 'fields' is set to 1, then the i-th
236: * field of the given speedo accessor may be accessed by the caller of this
237: * method.
238: */
239: public StateItf writeIntention(PersistentObjectItf sp,
240: long[] fields, Object thinLock) {
241: if (!sp.speedoIsActive()) {
242: StateItf sa = sp.speedoGetReferenceState();
243: if (sa == null) {
244: sp.speedoSetReferenceState(sp.speedoCreateState());
245: }
246: return sa;
247: }
248: POManagerItf pm = pmf.lookup();
249: if (pm == null) {
250: throw personality
251: .newUserRuntimeException("When a persistent object is used (read), a PersistenceManager/EntityManager is needed");
252: }
253: TransactionItf t = pm.getSpeedoTransaction();
254: try {
255: sendEvent(PRE_DIRTY, sp, null);
256: StateItf sa = (StateItf) tpm.writeIntention(t, sp,
257: isFieldLockingLevel ? thinLock : null);
258: sa.loadFields(pm, fields);
259: sendEvent(POST_DIRTY, sp, null);
260: return sa;
261: } catch (RolledBackPersistenceException e) {
262: throw t.rollBackOnInternalError(e);
263: } catch (PersistenceException e) {
264: throw personality.newRuntimeException(
265: "Impossible to notify a write intention",
266: ExceptionHelper.getNested(e));
267: }
268: }
269:
270: /**
271: * It retrieves the StateItf instance used in the current context.
272: * If the po is not active then the reference accessor is returned.
273: * If there is an active then the $classNameFields used in the context is
274: * returned. Be careul, if the persistent object is not used in the current
275: * context, then a null value will be returned, because no StateItf
276: * is registered in the working set.
277: */
278: public final StateItf getState(PersistentObjectItf sp) {
279: if (!sp.speedoIsActive()) {
280: return sp.speedoGetReferenceState();
281: }
282: POManagerItf pm = pmf.lookup();
283: if (pm == null) {
284: throw personality
285: .newUserRuntimeException("When a persistent object is used (read or write), a PersistenceManager is needed");
286: }
287: return (StateItf) pm.getSpeedoTransaction().lookup(
288: sp.getPName());
289: }
290:
291: public final PersistentObjectItf detachCopy(PersistentObjectItf sp,
292: POManagerItf pm, Map map, Object clone, Collection fgHints) {
293: Logger logger = getLogger();
294: if (Debug.ON && logger != null
295: && logger.isLoggable(BasicLevel.DEBUG)) {
296: logger.log(BasicLevel.DEBUG, "detachCopy()");
297: }
298: if (!isAbstract() && clone == null) {
299: try {
300: //instanciate the PersistentObjectItf clone
301: clone = newSpeedoPOInstance(sp.getClass());
302: PersistentObjectItf spClone = (PersistentObjectItf) clone;
303: spClone.speedoSetEncodedPName(pm.getEncodedPName(sp));
304: spClone.setCeAge(0);
305: spClone.speedoIsActive(false);
306: //instanciate the clone of the fields
307: StateItf fieldsClone = sp.speedoCreateState();
308: spClone.speedoSetReferenceState(fieldsClone);
309: fieldsClone.setSpeedoPO(spClone);
310: //put the association between the po and its clone into the map
311: if (map != null)
312: map.put(sp, clone);
313: synchronized (fgHints) {
314: readIntention(sp, null).detachCopy(pm, map,
315: fieldsClone, fgHints);
316: }
317: //mark the state as detached
318: fieldsClone
319: .setDetachedStatus(DetachedLifeCycle.DETACHED_CLEAN);
320: } catch (Exception e) {
321: throw personality
322: .newUserRuntimeException(
323: "Detach cannot be performed",
324: new Exception[] { ExceptionHelper
325: .getNested(e) });
326: }
327: }
328: return (PersistentObjectItf) clone;
329: }
330:
331: public final void attachCopy(PersistentObjectItf sp,
332: POManagerItf pm, Map map, Object clone, StateItf sa) {
333:
334: if (map != null) {
335: if ((PersistentObjectItf) map.get(clone) == sp)
336: return;
337: map.put(clone, sp);
338: //check the validity version
339: if (!sa.checkVersion(((PersistentObjectItf) clone)
340: .speedoGetReferenceState())) {
341: throw personality
342: .newRuntimeException("The detached copy is no more valid.");
343: }
344: sa.attachCopy(pm, map, ((PersistentObjectItf) clone)
345: .speedoGetReferenceState());
346: }
347: }
348:
349: public final Collection fgGetFieldsToLoad(String fgName,
350: boolean onlyDirectRef) {
351: Collection c = fgGetFieldsToLoad(fgName);
352: if (onlyDirectRef) {
353: Iterator it = c.iterator();
354: while (it.hasNext()) {
355: String s = (String) it.next();
356: if (s.indexOf(SpeedoFetchGroup.FG_DOT) != -1
357: || s.indexOf(SpeedoFetchGroup.FG_SHARP) != -1
358: || s.indexOf(SpeedoFetchGroup.FG_SLASH) != -1
359: || s.indexOf(SpeedoFetchGroup.FG_KEY) != -1
360: || s.indexOf(SpeedoFetchGroup.FG_VALUE) != -1) {
361: it.remove();
362: }
363: }
364: }
365: return c;
366: }
367:
368: public void setPrefetchOnExtent(boolean prefetch) {
369: this .prefetchOnExtent = prefetch;
370: }
371:
372: public boolean getPrefetchOnExtent() {
373: return prefetchOnExtent;
374: }
375:
376: public void setPrefetchOnGenClass(boolean prefetch) {
377: this .prefetchOnGenClass = prefetch;
378: }
379:
380: public boolean getPrefetchOnGenClass() {
381: return prefetchOnGenClass;
382: }
383:
384: public void setPrefetchOnQuery(boolean prefetch) {
385: this .prefetchOnQuery = prefetch;
386: }
387:
388: public boolean getPrefetchOnQuery() {
389: return prefetchOnQuery;
390: }
391:
392: public void initSH() {
393: }
394:
395: public Class getClassForQuery(String className, String queryName) {
396: try {
397: return getClass().getClassLoader().loadClass(className);
398: } catch (ClassNotFoundException e) {
399: throw personality
400: .newUserRuntimeException(
401: "Class '"
402: + className
403: + "' used in the query '"
404: + queryName
405: + "' defined in by the class '"
406: + getClassName()
407: + "' is not available. See the inner class loading problem: ",
408: e);
409: }
410: }
411:
412: public final QueryDefinition addNamedQuery(String name,
413: QueryDefinition query) {
414: return (QueryDefinition) namedQueries.put(name, query);
415: }
416:
417: public final QueryDefinition removeNamedQuery(String name) {
418: return (QueryDefinition) namedQueries.remove(name);
419: }
420:
421: public final QueryDefinition getNamedQuery(String name) {
422: return (QueryDefinition) namedQueries.get(name);
423: }
424:
425: public String getPath() {
426: return getClassName();
427: }
428:
429: //IMPLEMENTATION OF THE UserCacheHelper INTERFACE //
430: //------------------------------------------------//
431:
432: protected UserCache[] userCaches = new UserCache[0];
433:
434: protected synchronized UserCache addUserCache(String userCacheName,
435: String[] fields, int id) {
436: UserCacheImpl uc = new UserCacheImpl();
437: uc.setName(userCacheName);
438: uc.setIndexFieldNames(fields);
439: uc.setId(id);
440: uc.setActive(true);
441: if (id >= userCaches.length) {
442: UserCache[] newUserCaches = new UserCache[id + 1];
443: System.arraycopy(userCaches, 0, newUserCaches, 0,
444: userCaches.length);
445: userCaches = newUserCaches;
446: }
447: userCaches[id] = uc;
448: return uc;
449: }
450:
451: /**
452: * This method is implemented by the generated XXXHome class
453: */
454: public boolean activeUserCache(String cacheName) {
455: return false;
456: }
457:
458: public UserCache getUserCache(int cacheId) {
459: if (cacheId < 0 || cacheId >= userCaches.length
460: || userCaches[cacheId] == null) {
461: return null;
462: }
463: return userCaches[cacheId];
464: }
465:
466: public UserCache getUserCache(Collection fieldNames) {
467: PClassMapping[] pcms = null;
468: try {
469: pcms = getSubPCMs();
470: } catch (PException e) {
471: throw personality.newRuntimeException(
472: "Impossible to fetch home of sub class of "
473: + getClassName(), e);
474: }
475: if (pcms == null || pcms.length == 0) {
476: return getUserCacheOfTheClass(fieldNames);
477: }
478: ArrayList ucs = null;
479: //get the user cache of this class
480: UserCache uc = getUserCacheOfTheClass(fieldNames);
481: //if it not null, add it to the list
482: if (uc != null) {
483: if (ucs == null) {
484: ucs = new ArrayList();
485: }
486: ucs.add(uc);
487: }
488: //then do the same for each subclass
489: for (int i = 0; i < pcms.length; i++) {
490: uc = ((AbstractHomeImpl) pcms[i])
491: .getUserCacheOfTheClass(fieldNames);
492: if (uc != null) {
493: if (ucs == null) {
494: ucs = new ArrayList();
495: }
496: ucs.add(uc);
497: }
498: }
499: if (ucs != null) {
500: return new CompositeUserCache((UserCache[]) ucs
501: .toArray(new UserCache[ucs.size()]));
502: }
503: return null;
504: }
505:
506: private UserCache getUserCacheOfTheClass(Collection fieldNames) {
507: for (int i = 0; i < userCaches.length; i++) {
508: if (userCaches[i] != null && userCaches[i].isActive()) {
509: String[] fns = userCaches[i].getIndexFieldNames();
510: List l = Arrays.asList(fns);
511: if (fns.length == fieldNames.size()
512: && fieldNames.containsAll(l)
513: && l.containsAll(fieldNames)) {
514: return userCaches[i];
515: }
516: }
517: }
518: return null;
519: }
520:
521: public Collection getActiveUserCache() {
522: if (userCaches == null || userCaches.length == 0) {
523: return Collections.EMPTY_LIST;
524: }
525: ArrayList al = new ArrayList();
526: for (int i = 0; i < userCaches.length; i++) {
527: if (userCaches[i] != null && userCaches[i].isActive()) {
528: al.add(userCaches[i]);
529: }
530: }
531: return al;
532: }
533:
534: public void userCacheEntryUnbound(Object oid) {
535: if (userCaches == null || userCaches.length == 0) {
536: return;
537: }
538: for (int i = 0; i < userCaches.length; i++) {
539: if (userCaches[i] != null && userCaches[i].isActive()) {
540: userCaches[i].unbindFromOID(oid);
541: }
542: }
543: }
544: }
|