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.mapper.lib;
018:
019: import org.objectweb.fractal.api.control.BindingController;
020: import org.objectweb.jorm.api.PAccessor;
021: import org.objectweb.jorm.api.PBinding;
022: import org.objectweb.jorm.api.PException;
023: import org.objectweb.jorm.api.PExceptionNoDSI;
024: import org.objectweb.jorm.naming.api.PName;
025: import org.objectweb.perseus.persistence.api.ConnectionHolder;
026: import org.objectweb.perseus.persistence.api.NoDSIPersistenceException;
027: import org.objectweb.perseus.persistence.api.PersistenceException;
028: import org.objectweb.perseus.persistence.api.State;
029: import org.objectweb.perseus.persistence.api.StorageManager;
030: import org.objectweb.perseus.persistence.api.WorkingSet;
031: import org.objectweb.speedo.api.ExceptionHelper;
032: import org.objectweb.speedo.mapper.api.JormFactory;
033: import org.objectweb.speedo.mim.api.HomeItf;
034: import org.objectweb.speedo.mim.api.LifeCycle;
035: import org.objectweb.speedo.mim.api.PersistentObjectItf;
036: import org.objectweb.speedo.mim.api.StateItf;
037: import org.objectweb.util.monolog.api.BasicLevel;
038: import org.objectweb.util.monolog.api.Logger;
039:
040: import java.util.HashMap;
041: import java.util.HashSet;
042: import java.util.Iterator;
043: import java.util.Map;
044: import java.util.Set;
045:
046: /**
047: * This class is an implementation of the StorageManager interface based on
048: * Jorm.The single hypothesis concerns the architecture of the CacheEntry:
049: * - the CacheEntry implements the PBinding interface
050: * - the obj parameters implement StateItf and PAceessor.
051: *
052: * @author S.Chassande-Barrioz
053: */
054: public class JormStorageManager implements StorageManager,
055: BindingController {
056:
057: public final static String JORM_FACTORY_BINDING = "jorm-factory";
058: public final static String LOGGER_NAME = "org.objectweb.jorm.storageManager";
059:
060: protected JormFactory jormFactory = null;
061:
062: protected Logger logger = null;
063:
064: /**
065: * listes for each working set the persistent object removed in other
066: * working set. When a working set want to read a persistent object from
067: * the data support, a prefetch buffer can be used. But the prefetch buffer
068: * can contains a removed object.
069: */
070: protected Map ws2removedpo = new HashMap();
071:
072: //IMPLEMENTATION OF THE UserBindingControler INTERFACE //
073: //------------------------------------------------------//
074:
075: public String[] listFc() {
076: return new String[] { JORM_FACTORY_BINDING };
077: }
078:
079: public Object lookupFc(String s) {
080: if (JORM_FACTORY_BINDING.equals(s)) {
081: return jormFactory;
082: }
083: return null;
084: }
085:
086: public void bindFc(String s, Object o) {
087: if ("logger".equals(s)) {
088: logger = (Logger) o;
089: } else if (JORM_FACTORY_BINDING.equals(s)) {
090: jormFactory = (JormFactory) o;
091: }
092: }
093:
094: public void unbindFc(String s) {
095: if (JORM_FACTORY_BINDING.equals(s)) {
096: jormFactory = null;
097: }
098: }
099:
100: //IMPLEMENTATION OF THE StorageManager INTERFACE //
101: //-----------------------------------------------//
102:
103: public Object export(ConnectionHolder context, Object obj)
104: throws PersistenceException {
105: PBinding pb = (PBinding) obj;
106: try {
107: boolean flushed = false;
108: if (pb.getStatus() == PBinding.LIFECYCLE_DELTOWRITE) {
109: pb.write(context, (PAccessor) context.getWorkingSet()
110: .lookup(pb.getPName()));
111: flushed = true;
112: }
113: Object oid = pb.export(context);
114: if (!flushed) {
115: State s = context.getWorkingSet().lookup(oid);
116: if (s != null) {
117: PBinding oldObj = (PBinding) s.getCacheEntry()
118: .getCeObject();
119: if (oldObj != obj) {
120: oldObj.write(context, (PAccessor) s);
121: }
122: }
123: }
124: return oid;
125: } catch (PException e) {
126: throw new PersistenceException(e);
127: }
128: }
129:
130: public Object export(ConnectionHolder context, Object obj,
131: Object hints) throws PersistenceException {
132: PBinding pb = (PBinding) obj;
133: try {
134: boolean flushed = false;
135: if (pb.getStatus() == PBinding.LIFECYCLE_DELTOWRITE) {
136: pb.write(context, (PAccessor) context.getWorkingSet()
137: .lookup(pb.getPName()));
138: flushed = true;
139: }
140: Object oid = pb.export(context, hints);
141: if (!flushed) {
142: State s = context.getWorkingSet().lookup(oid);
143: if (s != null) {
144: PBinding oldObj = (PBinding) s.getCacheEntry()
145: .getCeObject();
146: if (oldObj != obj) {
147: oldObj.write(context, (PAccessor) s);
148: }
149: }
150: }
151: return oid;
152: } catch (PException e) {
153: throw new PersistenceException(e);
154: }
155: }
156:
157: public void unexport(ConnectionHolder context, Object oid)
158: throws PersistenceException {
159: if (logger.isLoggable(BasicLevel.DEBUG)) {
160: logger.log(BasicLevel.DEBUG, "unexport ctx=" + context
161: + " / oid=" + oid);
162: }
163: try {
164: ((PName) oid).unexport(context);
165: } catch (PException e) {
166: throw new PersistenceException(e);
167: }
168: registerUnexport(context.getWorkingSet(), oid);
169: }
170:
171: public void unexport(ConnectionHolder context, Object oid,
172: Object hints) throws PersistenceException {
173: if (logger.isLoggable(BasicLevel.DEBUG)) {
174: logger.log(BasicLevel.DEBUG, "unexport(hints) ctx="
175: + context + " / oid=" + oid);
176: }
177: try {
178: ((PName) oid).unexport(context, hints);
179: } catch (PException e) {
180: throw new PersistenceException(e);
181: }
182: registerUnexport(context.getWorkingSet(), oid);
183: }
184:
185: public void read(ConnectionHolder context, Object oid, State obj)
186: throws PersistenceException {
187: if (logger.isLoggable(BasicLevel.DEBUG))
188: logger.log(BasicLevel.DEBUG, "read ctx=" + context
189: + " / oid=" + oid + " / obj.class="
190: + obj.getClass().getName());
191: StateItf state = (StateItf) obj;
192: PersistentObjectItf pb = (PersistentObjectItf) state
193: .getSpeedoPO();
194: int speedoStatus = state.speedoGetStatus();
195: if (pb.getStatus() == PBinding.LIFECYCLE_DELTOWRITE
196: && speedoStatus != LifeCycle.PERSISTENT_DELETED
197: && speedoStatus != LifeCycle.PERSISTENT_NEW_DELETED) {
198: throw new PersistenceException(
199: "Concurrency problem, transaction must be rolledback");
200: }
201: try {
202: pb.read(context, state);
203: context.getWorkingSet().bind(state, oid,
204: WorkingSet.UNKNOWN_INTENTION);
205: //modified with rebind
206: state.indexFieldModified(Integer.MAX_VALUE, true);
207: pb.speedoGetHome().sendEvent(HomeItf.POST_LOAD, pb, null);
208: } catch (PExceptionNoDSI e) {
209: if (logger.isLoggable(BasicLevel.DEBUG)) {
210: logger.log(BasicLevel.DEBUG, "read ==> NO DSI");
211: }
212: throw new NoDSIPersistenceException(e);
213: } catch (PException e) {
214: Exception ie = ExceptionHelper.getNested(e);
215: logger.log(BasicLevel.ERROR, "read ctx=" + context
216: + " / oid=" + oid + " / obj.class="
217: + obj.getClass().getName(), ie);
218: throw new PersistenceException(ie);
219: }
220: }
221:
222: public void read(WorkingSet ws, Object oid, State obj,
223: boolean forUpdate) throws PersistenceException {
224: Object conn = ws.getConnectionHolder().getCHConnectionForRead();
225: if (logger.isLoggable(BasicLevel.DEBUG))
226: logger.log(BasicLevel.DEBUG, "read conn=" + conn
227: + " / oid=" + oid + " / obj.class="
228: + obj.getClass().getName() + " / tx=" + ws);
229: StateItf state = (StateItf) obj;
230: PersistentObjectItf pb = (PersistentObjectItf) state
231: .getSpeedoPO();
232: int speedoStatus = state.speedoGetStatus();
233: if (pb.getStatus() == PBinding.LIFECYCLE_DELTOWRITE
234: && speedoStatus != LifeCycle.PERSISTENT_DELETED
235: && speedoStatus != LifeCycle.PERSISTENT_NEW_DELETED) {
236: throw new PersistenceException(
237: "Concurrency problem, transaction must be rolledback");
238: }
239: Object ctx = usePrefetchBuffer(ws, oid) ? ws : null;
240: try {
241: pb.read(conn, state, ctx, forUpdate);
242: ws.bind(state, oid, WorkingSet.UNKNOWN_INTENTION);
243: //modified with rebind
244: state.indexFieldModified(Integer.MAX_VALUE, true);
245: pb.speedoGetHome().sendEvent(HomeItf.POST_LOAD, pb, null);
246: } catch (PExceptionNoDSI e) {
247: if (logger.isLoggable(BasicLevel.DEBUG)) {
248: logger.log(BasicLevel.DEBUG, "read ==> NO DSI");
249: }
250: throw new NoDSIPersistenceException(e);
251: } catch (PException e) {
252: Exception ie = ExceptionHelper.getNested(e);
253: logger.log(BasicLevel.ERROR, "read conn=" + conn
254: + " / oid=" + oid + " / obj.class="
255: + obj.getClass().getName() + " / tx=" + ws, ie);
256: throw new PersistenceException(ie);
257: }
258: }
259:
260: public void write(ConnectionHolder context, Object oid, State obj)
261: throws PersistenceException {
262: PersistentObjectItf pb = (PersistentObjectItf) obj
263: .getCacheEntry();
264: if (logger.isLoggable(BasicLevel.DEBUG)) {
265: logger.log(BasicLevel.DEBUG, "write ctx=" + context
266: + " / oid=" + oid + " / obj.class="
267: + obj.getClass().getName() + " / binding.status="
268: + pb.getStatus());
269: }
270: int s = pb.getStatus();
271: int preEvent = 0;
272: int postEvent = 0;
273: switch (s) {
274: case PBinding.LIFECYCLE_ACTIVEFORIO:
275: preEvent = HomeItf.PRE_UPDATE;
276: postEvent = HomeItf.POST_UPDATE;
277: break;
278: case PBinding.LIFECYCLE_NEWTOWRITE:
279: preEvent = HomeItf.PRE_CREATE;
280: postEvent = HomeItf.POST_CREATE;
281: break;
282: case PBinding.LIFECYCLE_DELTOWRITE:
283: preEvent = HomeItf.PRE_DELETE;
284: postEvent = HomeItf.POST_DELETE;
285: break;
286: }
287: if (preEvent != 0 && postEvent != 0) {
288: pb.speedoGetHome().sendEvent(preEvent, pb, null);
289: try {
290: pb.write(context, (PAccessor) obj);
291: } catch (PExceptionNoDSI e) {
292: throw new NoDSIPersistenceException(e);
293: } catch (PException e) {
294: throw new PersistenceException(e);
295: }
296: pb.speedoGetHome().sendEvent(postEvent, pb, null);
297: }
298: }
299:
300: public void beginWS(WorkingSet ws) {
301: synchronized (ws2removedpo) {
302: ws2removedpo.put(ws, new RemovedPOMemento(ws));
303: if (logger.isLoggable(BasicLevel.DEBUG)) {
304: logger.log(BasicLevel.DEBUG, "Begin of WS" + "\n\t-ws="
305: + ws);
306: }
307: }
308: }
309:
310: /**
311: * forget the working set
312: */
313: public void endWS(WorkingSet ws) {
314: final boolean debug = logger.isLoggable(BasicLevel.DEBUG);
315: synchronized (ws2removedpo) {
316: RemovedPOMemento mem = (RemovedPOMemento) ws2removedpo
317: .remove(ws);
318: StringBuffer sb = null;
319: if (mem.removedPOFromWS != null) {
320: for (Iterator it = ws2removedpo.values().iterator(); it
321: .hasNext();) {
322: RemovedPOMemento _mem = (RemovedPOMemento) it
323: .next();
324: _mem.addRemovedPOFromOtherWS(mem.removedPOFromWS);
325: if (debug) {
326: if (sb == null) {
327: sb = new StringBuffer();
328: sb.append("Add\n\t-oids= ");
329: sb.append(mem.removedPOFromWS);
330: }
331: sb.append("\n\t-ws= " + _mem.ws);
332: }
333: }
334: if (debug && sb != null) {
335: logger.log(BasicLevel.DEBUG, sb.toString());
336: }
337: }
338: }
339: }
340:
341: private void registerUnexport(WorkingSet ws, Object oid) {
342: synchronized (ws2removedpo) {
343: RemovedPOMemento rpo = (RemovedPOMemento) ws2removedpo
344: .get(ws);
345: if (rpo != null) {
346: rpo.registerRemovedPOFromWS(oid);
347: if (logger.isLoggable(BasicLevel.DEBUG)) {
348: logger.log(BasicLevel.DEBUG,
349: "Register the removed object:"
350: + "\n\t-oid=" + oid + "\n\t-ws="
351: + ws);
352: }
353: } else {
354: logger.log(BasicLevel.WARN,
355: "No POM registerd for adding the removed PO:"
356: + "\n\t-oid=" + oid + "\n\t-ws=" + ws);
357: }
358: }
359: }
360:
361: /**
362: * check if the persistent object has not been removed in another context.
363: * Indeed in case of Jorm uses prefetch buffers, the persistent object will
364: * be loaded even if it has been removed from the data base.
365: **/
366: private boolean usePrefetchBuffer(WorkingSet ws, Object oid) {
367: synchronized (ws2removedpo) {
368: RemovedPOMemento rpo = (RemovedPOMemento) ws2removedpo
369: .get(ws);
370: if (rpo == null) {
371: StringBuffer sb = new StringBuffer();
372: sb
373: .append("Internal Warning: No POM found for checking removed POs then Speedo does not use prefetchbuffer optimisation, and reloads data from the database");
374: sb.append("\n\t-oid=").append(oid);
375: sb.append("\n\t-ws=").append(ws);
376: Exception e = new Exception(sb.toString());
377: logger.log(BasicLevel.WARN, e.getMessage(), e);
378: return false;
379: } else {
380: boolean res = !rpo.isRemoved(oid);
381: if (logger.isLoggable(BasicLevel.DEBUG)) {
382: logger.log(BasicLevel.DEBUG, (res ? "" : "NOT ")
383: + "USE prefetch buffer" + "\n\t-oid=" + oid
384: + "\n\t-ws= " + ws);
385: }
386: return res;
387: }
388: }
389: }
390: }
391:
392: /**
393: * This structure registers the removed objects from a particular working set.
394: * So an instance of this class has to be associated to a working set.
395: *
396: * @author S.Chassande-Barrioz
397: */
398: class RemovedPOMemento {
399: /**
400: * contains the identifiers of persitent objects which have been removed
401: * from the working set linked to this.
402: * this field is used at the end of a working set to transmit the list of
403: * removed object from this working set.
404: */
405: public HashSet removedPOFromWS = null;
406:
407: /**
408: * contains the identifiers of persistent objects which have been removed
409: * from the working set linked to this or from another working set.
410: * this field is used to choose if it is possible to use a prefetch buffer
411: * for a given identifier
412: */
413: public HashSet removedPO = null;
414:
415: public final WorkingSet ws;
416:
417: public RemovedPOMemento(WorkingSet _ws) {
418: this .ws = _ws;
419: }
420:
421: /**
422: * register the unexport of a persistent object from the linked working set
423: * @param oid is the identifier of the unexported persistent object
424: */
425: public void registerRemovedPOFromWS(Object oid) {
426: if (removedPOFromWS == null) {
427: removedPOFromWS = new HashSet();
428: }
429: removedPOFromWS.add(oid);
430: if (removedPO == null) {
431: removedPO = new HashSet();
432: }
433: removedPO.add(oid);
434: }
435:
436: /**
437: * Keeps in memory that persistent objects has been removed from another
438: * working set
439: * @param oids is the set of identifier of persistent object which have
440: * been unexported in another working set.
441: */
442: public void addRemovedPOFromOtherWS(Set oids) {
443: if (removedPO == null) {
444: removedPO = new HashSet();
445: }
446: removedPO.addAll(oids);
447: }
448:
449: /**
450: * Indicates if a persistent object has been removed from the current
451: * working set or another.
452: * @param oid is the identifier of the persistent object which could be
453: * previously removed.
454: */
455: public boolean isRemoved(Object oid) {
456: return removedPO != null && removedPO.contains(oid);
457: }
458: }
|