/* * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. * * This file is part of ZooDB. * * ZooDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZooDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZooDB. If not, see <http://www.gnu.org/licenses/>. * * See the README and COPYING files for further information. */ package org.zoodb.internal.client; import java.util.Arrays; import javax.jdo.listener.ClearLifecycleListener; import javax.jdo.listener.CreateLifecycleListener; import javax.jdo.listener.DeleteLifecycleListener; import javax.jdo.listener.DetachLifecycleListener; import javax.jdo.listener.DirtyLifecycleListener; import javax.jdo.listener.InstanceLifecycleEvent; import javax.jdo.listener.InstanceLifecycleListener; import javax.jdo.listener.LoadLifecycleListener; import javax.jdo.listener.StoreLifecycleListener; import org.zoodb.api.ZooInstanceEvent; import org.zoodb.api.impl.ZooPC; import org.zoodb.internal.DataDeleteSink; import org.zoodb.internal.DataEvictor; import org.zoodb.internal.DataIndexUpdater; import org.zoodb.internal.DataSink; import org.zoodb.internal.Node; import org.zoodb.internal.Session; import org.zoodb.internal.ZooClassDef; import org.zoodb.internal.util.DBLogger; /** * This bundles Class, Node and Session to a context for persistent objects. * * This is primarily an optimization, such that every persistent capable object PC needs only * one reference (to ClassNodeSessionBundle) instead of three to each of the above. At the moment, * this saves only 16byte per PC, but that is already considerable in cases with many little * objects (SNA: 50.000.000 PC - saves 800MB). * * TODO * In future this may also contain class extents per node, as required by the commit(), * evict(class) or possibly query methods. * * @author Tilmann Zaeschke */ public final class PCContext { private final Session session; private final Node node; private final ZooClassDef def; private final DataEvictor evictor; private final DataIndexUpdater updater; private final DataSink dataSink; private final DataDeleteSink dataDeleteSink; private InstanceLifecycleListener[] listeners = null; public PCContext(ZooClassDef def, Session session, Node node) { this.def = def; this.session = session; this.node = node; //only for non-schema classes this.evictor = new DataEvictor(def, session.getConfig().getEvictPrimitives()); this.updater = new DataIndexUpdater(def); //==null for schema bootstrapping TODO why? if (node != null) { dataSink = node.createDataSink(def); dataDeleteSink = node.createDataDeleteSink(def); } else { dataSink = null; dataDeleteSink = null; } } public final Session getSession() { return session; } public final Node getNode() { return node; } public final ZooClassDef getClassDef() { return def; } public final DataEvictor getEvictor() { return evictor; } public final DataIndexUpdater getIndexer() { return updater; } public DataSink getDataSink() { return dataSink; } public DataDeleteSink getDataDeleteSink() { return dataDeleteSink; } public void addLifecycleListener(InstanceLifecycleListener listener) { if (listeners == null) { listeners = new InstanceLifecycleListener[0]; } for (InstanceLifecycleListener l: listeners) { if (l.equals(listener)) { throw DBLogger.newUser("Listener already registered for class " + def.getClassName()); } } listeners = Arrays.copyOf(listeners, listeners.length + 1); listeners[listeners.length-1] = listener; } public void removeLifecycleListener(InstanceLifecycleListener listener) { if (listeners == null) { return; } for (int i = 0; i < listeners.length; i++) { InstanceLifecycleListener l = listeners[i]; if (l.equals(listener)) { if (listeners.length > 1) { if (i + 1 < listeners.length) { System.arraycopy(listeners, i+1, listeners, i, listeners.length-i-1); } listeners = Arrays.copyOf(listeners, listeners.length -1); } else { listeners = null; } break; } } } public void notifyEvent(ZooPC src, ZooInstanceEvent event) { if (def.getSuperDef() != null) { def.getSuperDef().getProvidedContext().notifyEvent(src, event); } if (listeners == null || src.getClass() == ZooClassDef.class) { //Ignore this for system objects, even if the listeners are stored in a system objects return; } //TODO this is a bit dirty, move classes into enum? for (InstanceLifecycleListener l: listeners) { switch (event) { case PRE_CLEAR: if (ClearLifecycleListener.class.isAssignableFrom(l.getClass())) { ((ClearLifecycleListener)l).preClear( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.CLEAR)); } break; case POST_CLEAR: if (ClearLifecycleListener.class.isAssignableFrom(l.getClass())) { ((ClearLifecycleListener)l).postClear( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.CLEAR)); } break; case CREATE: if (CreateLifecycleListener.class.isAssignableFrom(l.getClass())) { ((CreateLifecycleListener)l).postCreate( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.CREATE)); } break; case PRE_DELETE: if (DeleteLifecycleListener.class.isAssignableFrom(l.getClass())) { ((DeleteLifecycleListener)l).preDelete( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.DELETE)); } break; case POST_DELETE: if (DeleteLifecycleListener.class.isAssignableFrom(l.getClass())) { ((DeleteLifecycleListener)l).postDelete( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.DELETE)); } break; case PRE_DETACH: if (DetachLifecycleListener.class.isAssignableFrom(l.getClass())) { ((DetachLifecycleListener)l).preDetach( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.DETACH, src)); } break; case POST_DETACH: if (DetachLifecycleListener.class.isAssignableFrom(l.getClass())) { ((DetachLifecycleListener)l).postDetach( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.DETACH, src)); } break; case PRE_DIRTY: if (DirtyLifecycleListener.class.isAssignableFrom(l.getClass())) { ((DirtyLifecycleListener)l).preDirty( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.DIRTY)); } break; case POST_DIRTY: if (DirtyLifecycleListener.class.isAssignableFrom(l.getClass())) { ((DirtyLifecycleListener)l).postDirty( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.DIRTY)); } break; case LOAD: if (LoadLifecycleListener.class.isAssignableFrom(l.getClass())) { ((LoadLifecycleListener)l).postLoad( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.LOAD)); } break; case PRE_STORE: if (StoreLifecycleListener.class.isAssignableFrom(l.getClass())) { ((StoreLifecycleListener)l).preStore( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.STORE)); } break; case POST_STORE: if (StoreLifecycleListener.class.isAssignableFrom(l.getClass())) { ((StoreLifecycleListener)l).postStore( new InstanceLifecycleEvent(src, InstanceLifecycleEvent.STORE)); } break; default: throw new IllegalArgumentException(event.name()); } } } }