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.fractal.api.control.BindingController;
020: import org.objectweb.jorm.api.PClassMapping;
021: import org.objectweb.jorm.api.PException;
022: import org.objectweb.jorm.lib.JormPathHelper;
023: import org.objectweb.jorm.naming.api.PBinder;
024: import org.objectweb.jorm.naming.api.PName;
025: import org.objectweb.jorm.naming.api.PNameManager;
026: import org.objectweb.jorm.type.api.PType;
027: import org.objectweb.jorm.util.api.Loggable;
028: import org.objectweb.perseus.cache.api.CacheEntry;
029: import org.objectweb.perseus.cache.api.CacheEntryFactory;
030: import org.objectweb.perseus.cache.api.CacheEvent;
031: import org.objectweb.perseus.cache.api.FixableCacheEntry;
032: import org.objectweb.perseus.persistence.api.ConnectionHolder;
033: import org.objectweb.perseus.persistence.api.MemoryInstanceManager;
034: import org.objectweb.perseus.persistence.api.PersistenceException;
035: import org.objectweb.perseus.persistence.api.State;
036: import org.objectweb.perseus.persistence.api.StateManager;
037: import org.objectweb.speedo.api.ExceptionHelper;
038: import org.objectweb.speedo.api.SpeedoRuntimeException;
039: import org.objectweb.speedo.genclass.AbstractGenClassHome;
040: import org.objectweb.speedo.genclass.SupportedGenClass;
041: import org.objectweb.speedo.genclass.api.SpeedoGenClassPO;
042: import org.objectweb.speedo.lib.Personality;
043: import org.objectweb.speedo.mapper.api.JormFactory;
044: import org.objectweb.speedo.mim.api.LifeCycle;
045: import org.objectweb.speedo.mim.api.MemoryInstanceManagerAttribute;
046: import org.objectweb.speedo.mim.api.StateItf;
047: import org.objectweb.speedo.mim.api.HomeItf;
048: import org.objectweb.speedo.mim.api.PersistentObjectItf;
049: import org.objectweb.util.monolog.api.BasicLevel;
050: import org.objectweb.util.monolog.api.Logger;
051:
052: import java.util.Collections;
053: import java.util.HashMap;
054: import java.util.Iterator;
055: import java.util.Map;
056: import java.util.StringTokenizer;
057:
058: import javax.jdo.listener.InstanceLifecycleEvent;
059:
060: /**
061: * This class is an implementation of the MemoryInstanceManager provided by the
062: * persistence framwork availlable in perseus. This implementation depends on
063: * the identifier used, and the way to instanciate user objects. This
064: * implementation supports only the jorm object identifier: PName.
065: * This implementation supports also the instanciation of the Jorm generic
066: * class. To do this it is needed to specify for each jorm generic class used
067: * the name of the class which must be instanciated. This configuration is done
068: * via the GenClassNames attribute.
069: * This implementation does not used a pool, then the instance are created at
070: * each newInstance call.
071: *
072: * @author S.Chassande-Barrioz
073: */
074: public class MemoryInstanceManagerImpl implements
075: MemoryInstanceManager, MemoryInstanceManagerAttribute,
076: StateManager, CacheEntryFactory, BindingController {
077:
078: public final static String JORM_FACTORY_BINDING = "jorm-factory";
079:
080: /**
081: * This fields maintains the association between the name of the Jorm
082: * generic class and the class name which must be implemented.
083: */
084: private Map name2gcimpl = null;
085:
086: protected JormFactory jf = null;
087:
088: protected Personality personality;
089:
090: protected Logger logger = null;
091:
092: public MemoryInstanceManagerImpl() {
093: name2gcimpl = Collections.EMPTY_MAP;
094: }
095:
096: private String getGCClassName(String cn) {
097: String res = (String) name2gcimpl.get(cn);
098: if (res != null) {
099: return res;
100: }
101: for (int i = 0; i < SupportedGenClass.GC_IMPL.length; i++) {
102: if (SupportedGenClass.GC_IMPL[i][0].equals(cn)) {
103: return personality
104: .getGenClassName(SupportedGenClass.GC_IMPL[i][1]);
105: }
106: }
107: return null;
108: }
109:
110: /**
111: * Active a persistent object :
112: * - assign the PClassMapping
113: * - assign a Pbinding for the SpeedoGenClassPO
114: *
115: * @param sp is PersistentObjectItf or a SpeedoGenClassPO instance, the
116: * persistent object
117: * @param pn the identifier of the object
118: */
119: private void activePO(PersistentObjectItf sp, PName pn) {
120: //fetch the PClassMapping
121: PClassMapping pcm = null;
122: if (pn != null && (pn.getPNameManager() instanceof PBinder)) {
123: pcm = ((PBinder) pn.getPNameManager())
124: .getBinderClassMapping();
125: }
126: if (sp instanceof SpeedoGenClassPO) {
127: if (((SpeedoGenClassPO) sp).speedoGetPBinding() == null) {
128: if (pcm == null) {
129: pcm = jf.getGenClassMapping(((SpeedoGenClassPO) sp)
130: .speedoGetGenClassId());
131: }
132: //Assign the PBinding
133: try {
134: ((SpeedoGenClassPO) sp).speedoSetPBinding(pcm
135: .createPBinding());
136: } catch (PException e) {
137: throw personality
138: .newRuntimeException(
139: "Impossible to initialize the delegate PBinding of the genclass",
140: e);
141: }
142: if (sp instanceof Loggable) {
143: ((Loggable) sp).setLogger(logger);
144: }
145: }
146: } else {
147: if (pcm == null) {
148: try {
149: pcm = jf.getPClassMapping(sp.getClass());
150: } catch (PException e) {
151: throw personality.newRuntimeException(
152: "Impossible to initialize a class", e);
153: }
154: }
155: if (sp.getPClassMapping() == null) {
156: //Assign the PClassMapping
157: try {
158: sp.init(pcm);
159: } catch (PException e) {
160: Exception ie = ExceptionHelper.getNested(e);
161: if (logger != null)
162: logger.log(BasicLevel.ERROR,
163: "Impossible to init the persistent class: "
164: + sp.getClass().getName(), ie);
165: throw personality
166: .newRuntimeException("Impossible to init the persistent class:"
167: + ie.getMessage());
168: }
169: }
170: }
171: //Mark as active
172: sp.speedoIsActive(true);
173: if (logger != null && logger.isLoggable(BasicLevel.DEBUG))
174: logger.log(BasicLevel.DEBUG, "PO activated: "
175: + sp.getClass().getName());
176: }
177:
178: private HomeItf getHome(Object o) {
179: if (o == null) {
180: logger.log(BasicLevel.WARN,
181: "Impossible to find the home: null object");
182: return null;
183: } else if (o instanceof PersistentObjectItf) {
184: PersistentObjectItf po = (PersistentObjectItf) o;
185: HomeItf h = po.speedoGetHome();
186: if (h == null) {
187: if (o instanceof SpeedoGenClassPO) {
188: PName pn = po.getPName();
189: if (pn == null) {
190: h = (HomeItf) ((PBinder) pn.getPNameManager())
191: .getBinderClassMapping();
192: } else {
193: h = (HomeItf) jf
194: .getGenClassMapping(((SpeedoGenClassPO) po)
195: .speedoGetGenClassId());
196: }
197: } else {
198: try {
199: h = (HomeItf) jf.getPClassMapping(o.getClass());
200: } catch (PException e) {
201: throw new SpeedoRuntimeException(e);
202: }
203: }
204: }
205: if (h == null) {
206: logger.log(BasicLevel.WARN,
207: "Impossible to find the home");
208: }
209: return h;
210: } else if (o instanceof PName) {
211: PName pn = (PName) o;
212: PNameManager pnm = pn.getPNameManager();
213: while (!(pnm instanceof PBinder)) {
214: PName npn;
215: try {
216: npn = pn.resolve(null);
217: } catch (PException e) {
218: logger.log(BasicLevel.WARN,
219: "Impossible to find the home from the PName "
220: + pn, e);
221: return null;
222: }
223: if (npn == pn) {
224: logger.log(BasicLevel.WARN,
225: "Impossible to find the home from the PName "
226: + pn + " : cycle");
227: return null;
228: } else {
229: pnm = pn.getPNameManager();
230: }
231: }
232: return (HomeItf) ((PBinder) pnm).getBinderClassMapping();
233: } else {
234: logger.log(BasicLevel.WARN,
235: "No home found with this object: " + o.getClass());
236: return null;
237: }
238: }
239:
240: // IMPLEMENTATION OF THE MemoryInstanceManagerAttribute INTERFACE //
241: //----------------------------------------------------------------------//
242: public String[] listFc() {
243: return new String[] { JORM_FACTORY_BINDING };
244: }
245:
246: public Object lookupFc(String s) {
247: if (JORM_FACTORY_BINDING.equals(s)) {
248: return jf;
249: } else {
250: return null;
251: }
252: }
253:
254: public void bindFc(String s, Object o) {
255: if (JORM_FACTORY_BINDING.equals(s)) {
256: jf = (JormFactory) o;
257: } else if ("logger".equals(s)) {
258: logger = (Logger) o;
259: }
260: }
261:
262: public void unbindFc(String s) {
263: if (JORM_FACTORY_BINDING.equals(s)) {
264: jf = null;
265: }
266: }
267:
268: // IMPLEMENTATION OF THE MemoryInstanceManagerAttribute INTERFACE //
269: //----------------------------------------------------------------------//
270:
271: /**
272: * @return a String describing the genclass names with the following format:
273: * "(jorm_name,java_name),(jorm_name,java_name),(jorm_name,java_name)}"
274: */
275: public String getGenClassNames() {
276: if (name2gcimpl == null || name2gcimpl.size() == 0)
277: return "{}";
278: StringBuffer res = new StringBuffer("");
279: boolean first = true;
280: for (Iterator it = name2gcimpl.entrySet().iterator(); it
281: .hasNext();) {
282: if (first) {
283: res.append("{");
284: first = false;
285: } else
286: res.append(",");
287: res.append("(");
288: Map.Entry me = (Map.Entry) it.next();
289: res.append(me.getKey());
290: res.append(",");
291: res.append(me.getValue());
292: res.append(")");
293: }
294: res.append("}");
295: return res.toString();
296: }
297:
298: /**
299: * It assignes a description of the gen class names.
300: * @param gcname is the desciption which must follow this format:
301: * "(jorm_name,java_name),(jorm_name,java_name),(jorm_name,java_name)}"
302: */
303: public void setGenClassNames(String gcname) {
304: if (gcname == null || gcname.length() == 0)
305: name2gcimpl = Collections.EMPTY_MAP;
306: name2gcimpl = new HashMap(5);
307: StringTokenizer st = new StringTokenizer(gcname, "{,()}:;[]",
308: false);
309: String key = null;
310: while (st.hasMoreTokens()) {
311: if (key == null) {
312: key = st.nextToken().trim();
313: } else {
314: name2gcimpl.put(key, st.nextToken().trim());
315: }
316: }
317: }
318:
319: public Personality getPersonality() {
320: return personality;
321: }
322:
323: public void setPersonality(Personality p) {
324: this .personality = p;
325: }
326:
327: // IMPLEMENTATION OF THE MemoryInstanceManager INTERFACE //
328: //-------------------------------------------------------//
329:
330: /**
331: * It creates an instance since an identifier
332: * @param oid is the identifier of the futur object
333: * @return a memory instance
334: */
335: public Object newInstance(Object oid, ConnectionHolder context)
336: throws PersistenceException {
337: if (!(oid instanceof PName))
338: throw new PersistenceException(
339: "Unmanaged object identifier: " + oid);
340: PName pn = (PName) oid;
341: String className = null;
342: try {
343: pn = pn.resolve(context);
344: if (logger.isLoggable(BasicLevel.DEBUG)) {
345: logger.log(BasicLevel.DEBUG, "newInstance(" + oid
346: + "): pn.resolve=" + pn);
347: logger.log(BasicLevel.DEBUG, "newInstance(" + oid
348: + "): pn.type=" + pn.getPType());
349: }
350: PType type = pn.getPType();
351: if (type == null) {
352: //Error detected by Roland Hedayat
353: throw new PersistenceException("newInstance() "
354: + "\n\toid="
355: + oid
356: + "\n\tpn="
357: + pn
358: + "\n\tpn.pnc="
359: + (pn.getPNameManager() != null ? pn
360: .getPNameManager() : null));
361: }
362: className = type.getJormName();
363: int idx = className.indexOf(JormPathHelper.SEP);
364: PClassMapping pcm = ((PBinder) pn.getPNameManager())
365: .getBinderClassMapping();
366: PersistentObjectItf sp = null;
367: if ((pcm instanceof AbstractGenClassHome) && idx != -1) {
368: // This is a Generic class
369: className = getGCClassName(className.substring(0, idx));
370: sp = (PersistentObjectItf) Class.forName(className)
371: .newInstance();
372: } else {
373: if (pcm != null) {
374: className = pcm.getClassName();
375: }
376: ClassLoader cl = jf.getClassLoader(className);
377: if (cl == null) {
378: cl = oid.getClass().getClassLoader();
379: if (cl == null) {
380: cl = ClassLoader.getSystemClassLoader();
381: }
382: }
383: sp = (PersistentObjectItf) cl.loadClass(className)
384: .newInstance();
385: }
386: sp.speedoSetReferenceState(null);
387: return sp;
388: } catch (PException e) {
389: throw new PersistenceException(
390: "Impossible to analyze the object identifier" + oid,
391: e);
392: } catch (Exception e) {
393: throw new PersistenceException(
394: "Impossible to instanciate the class "
395: + className
396: + "Be careful to the generic class (Collection, ...), oid class:"
397: + oid.getClass(), e);
398: }
399: }
400:
401: public boolean isObjectSharing(Object o) {
402: HomeItf home = getHome(o);
403: return home == null || home.isShareable();
404: }
405:
406: // IMPLEMENTATION OF THE StateManager INTERFACE //
407: //----------------------------------------------//
408: public State createState(CacheEntry ce) {
409: return ((PersistentObjectItf) ce).speedoCreateState();
410: }
411:
412: public State createState(State s) {
413: PersistentObjectItf sp = (PersistentObjectItf) s
414: .getCacheEntry();
415: StateItf sa = sp.speedoCreateState();
416: sp.speedoCopyState(((StateItf) s), sa);
417: return sa;
418: }
419:
420: public State getReferenceState(CacheEntry ce) {
421: return ((PersistentObjectItf) ce).speedoGetReferenceState();
422: }
423:
424: public void destroyState(State state) {
425: //nothing to do
426: }
427:
428: public void makeUnexported(State state) {
429: ((StateItf) state)
430: .speedoChangeStatus(LifeCycle.ACTION_DELETEPERSISTENT);
431: }
432:
433: public boolean isUnexported(State state) {
434: return LifeCycle
435: .isDeleted(((StateItf) state).speedoGetStatus());
436: }
437:
438: public void makeExported(State state) {
439: ((StateItf) state)
440: .speedoChangeStatus(LifeCycle.ACTION_MAKEPERSISTENT);
441: }
442:
443: public boolean isExported(State state) {
444: return LifeCycle.isNew(((StateItf) state).speedoGetStatus());
445: }
446:
447: public void makeDirty(State state) {
448: ((StateItf) state)
449: .speedoChangeStatus(LifeCycle.ACTION_WRITEFIELD_ACTIVEDATASTORETRANSACTION);
450: ((StateItf) state).setFlushed(false);
451: }
452:
453: public boolean isDirty(State state) {
454: return LifeCycle.isDirty(((StateItf) state).speedoGetStatus());
455: }
456:
457: public void setReferenceState(CacheEntry ce, State state) {
458: if (state == ((PersistentObjectItf) ce)
459: .speedoGetReferenceState()) {
460: return;
461: }
462: PersistentObjectItf sp = (PersistentObjectItf) ce;
463: if (sp.speedoIsActive() && state == null) {
464: sp.speedoGetHome().sendEvent(HomeItf.PRE_CLEAR, sp, null);
465: }
466: sp.speedoSetReferenceState((StateItf) state);
467: if (sp.speedoIsActive() && state == null) {
468: sp.speedoGetHome().sendEvent(HomeItf.POST_CLEAR, sp, null);
469: }
470: }
471:
472: public void makeClean(State state) {
473: StateItf sa = (StateItf) state;
474: sa.speedoSetStatus(LifeCycle.PERSISTENT_CLEAN);
475: }
476:
477: public void makeFlushed(State state) {
478: ((StateItf) state).setFlushed(true);
479: }
480:
481: public boolean isFlushed(State state) {
482: return ((StateItf) state).hasBeenFlush();
483: }
484:
485: public void makeUnbound(CacheEntry ce) {
486: PersistentObjectItf sp = (PersistentObjectItf) ce;
487: sp.speedoIsActive(false);
488: try {
489: sp.init(null);
490: } catch (PException e) {
491: logger.log(BasicLevel.WARN, "Error during the unbinding: ",
492: e);
493: }
494: }
495:
496: public void makeBound(CacheEntry ce, Object oid) {
497: PName pname = (PName) oid;
498: PersistentObjectItf sp = (PersistentObjectItf) ce;
499: try {
500: sp.bind(pname);
501: } catch (PException e) {
502: logger
503: .log(BasicLevel.WARN, "Error during the binding: ",
504: e);
505: }
506: }
507:
508: public boolean isBound(CacheEntry ce) {
509: return ((PersistentObjectItf) ce).speedoIsActive();
510: }
511:
512: public boolean isToMerge(State state) {
513: return ((StateItf) state).isToMerge();
514: }
515:
516: public void makeToMerge(State state, Object thinLock) {
517: ((StateItf) state).makeToMerge(thinLock);
518: }
519:
520: public State merge(State oldState, State newState) {
521: return ((StateItf) newState).merge(oldState);
522: }
523:
524: public void stateNoMoreUsed(State state) {
525: ((StateItf) state).unSwizzle();
526: }
527:
528: // IMPLEMENTATION OF THE CacheEventListener INTERFACE //
529: //----------------------------------------------------//
530:
531: /**
532: * @param event
533: */
534: public void entryBound(CacheEvent event) {
535: }
536:
537: /**
538: * @param event
539: */
540: public void entryUnbound(CacheEvent event) {
541: if (logger.isLoggable(BasicLevel.DEBUG)) {
542: logger.log(BasicLevel.DEBUG,
543: "Entry unbound from the cache: \n\tid: "
544: + event.getCeIdentifier());
545: }
546: PName pn = (PName) event.getCeIdentifier();
547: HomeItf sh = (HomeItf) ((PBinder) pn.getPNameManager())
548: .getBinderClassMapping();
549: sh.userCacheEntryUnbound(pn);
550: }
551:
552: // IMPLEMENTATION OF THE CacheEntryFactory INTERFACE //
553: //---------------------------------------------------//
554:
555: public int getCachingStrategy(Object o) {
556: HomeItf home = getHome(o);
557: if (home == null) {
558: return CACHING_STRATEGY_CACHED;
559: } else {
560: return home.isCacheable() ? CACHING_STRATEGY_CACHED
561: : CACHING_STRATEGY_NO_CACHE;
562: }
563: }
564:
565: /**
566: * binds the PersistentObjectItf to its identifier (PName) if it does not have
567: * already one.
568: * @param id is the PName of the PersistentObjectItf
569: * @param obj is the PersistentObjectItf instance added into the cache
570: * @return the PersistentObjectItf instance
571: */
572: public FixableCacheEntry create(Object id, Object obj) {
573: PersistentObjectItf sp = (PersistentObjectItf) obj;
574: PName pn = (PName) id;
575: activePO(sp, pn);
576: if (sp.getPName() == null || sp.getPName().isNull()) {
577: try {
578: pn.resolve(null);
579: sp.bind(pn);
580: } catch (PException e) {
581: throw personality
582: .newRuntimeException(
583: "Impossible to bind the po to its persistent name: ",
584: e);
585: }
586: }
587: /**
588: * else the entry is a new persistent object, the export created an
589: * identifier
590: */
591: return (FixableCacheEntry) obj;
592: }
593: }
|