All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.pdo.DomainContext Maven / Gradle / Ivy

There is a newer version: 21.16.2.0
Show newest version
/*
 * Tentackle - https://tentackle.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.tentackle.pdo;

import org.tentackle.misc.TrackedList;
import org.tentackle.session.PersistenceException;
import org.tentackle.session.SessionHolder;

import java.util.Collection;
import java.util.List;

/**
 * The domain context.
 * 

* Basically, a domain context is a {@link SessionHolder} with additional information * about the root entity and the application's context.
* All {@link PersistentDomainObject}s must refer to a {@code DomainContext}. * The DomainContext carries the application's higher level context * which can be used to hold domain-related information, for example to implement multi-tenancy. * As this is application specific, it is recommended to provide an application specific context * via a {@link DomainContextFactory}. *

* By default, domain contexts are unnamed. By creating a named clone, the new context becomes the root of an application-specific sub context. * This sub context usually describes a distinct part of the application, like a module, task or purpose. * Named contexts can be used, for example, to inhibit or allow invocation of certain methods. * Another purpose is to ensure that a PDO being locked in a named context cannot be locked, * persisted or unlocked by the same user in another named context. Sub contexts may have any number of other sub contexts. *

* Furthermore, since all PDOs must be either a root-entity or a direct or indirect component of a root-entity, * each root-entity maintains its own copy of the domain context, which is known as * the root-context. The root-entity and all of its components refer to that root-context and therefore * "know" their root entity. This information is used, for example, by the {@link org.tentackle.security.SecurityManager} * and when persisting child components at level 2 and greater (via the optional column {@code rootId}). *

* The DomainContext can be extended to hold the ID(s) of the object(s) describing the context in multi-tenant systems. * Because a {@link PersistentDomainObject} carries a reference to that context, it can serve as * a root for a logical data space. For example, a {@link PersistentObject#selectAll()} * of accounts will deliver only the accounts belonging to that particular space.
* All classes depending on those extended contexts must also implement * an appropriate extension of {@link DomainContextDependable}. By further subclassing, * a context hierarchy can be achieved. For example, in a financial * accounting system, the hierarchy could be: *

 * TenantContext -> FiscalYearContext -> BookContext
 * 
* with corresponding *
 * TenantDependable -> FiscalYearDependable -> BookDependable.
 * 
* and the root entities of that contexts *
 * Tenant -> FiscalYear -> Book.
 * 
*

* The domain model supports multi-tenancy by means of the option {@code CONTEXT}. * When generating the finders, the {@code WHERE}-clause is automatically {@code AND}ed with the context column * using the value from the domain context. *

* The domain context may also be used to hold references to objects and collections of objects * that belong to the same root entity (a.k.a. "entity context"). * * @author harald */ public interface DomainContext extends SessionHolder, Comparable, Cloneable { /** * Clones this context. *

* The clone gets the root entity and sessionImmutable cleared. * @return the context's clone */ DomainContext clone(); /** * Clones this context.
* Same as {@link #clone()}, but keeps the root entity, if any is set. * * @return the context's clone */ DomainContext cloneKeepRoot(); /** * Clones this context with another context name. *

* Same as {@link #clone()}, but appends the given name to the current context names.
* The new name must be different from the current ones, * not null, not containing only whitespaces and not containing a colon (: is used to concatenate the context names in toGenericString). * * @param name the name of the new context * @return the context's clone */ DomainContext clone(String name); /** * Clones this context with another context name.
* Same as {@link #clone(String)}, but keeps the root entity, if any is set. * * @param name the name of the new context * @return the context's clone */ DomainContext cloneKeepRoot(String name); /** * Gets the context this context was cloned from. * * @return null if not cloned or original context isn't available anymore (GC'd or was remote) */ DomainContext getClonedContext(); /** * Gets the names of this context. *

* Notice: the names must not be part in equals, hashCode or compareTo implementations, * because they denote only sub contexts. As a result, all sub contexts of the same * root context are considered logically equal from a multi-tenancy perspective. * * @return the unmodifiable list of names, never null, never empty, the first element is always the empty string */ List getNames(); /** * Gets a clone of this context with a thread-local session. *

* Notice: A thread-local domain context is always a non-root context. * * @return the context with a thread-local session */ DomainContext getThreadLocalSessionContext(); /** * Clears the cached clone of this context.
* Forces cloning on next {@link #getThreadLocalSessionContext()}. */ void clearThreadLocalSessionContext(); /** * Asserts that this context is allowed to be used by the current user.
* Only invoked when session is updated after deserialization (objects traveling between JVMs). *

* If it is not allowed, a {@link PersistenceException} must be thrown.
* Override the method in middle tier servers, usually only necessary in multi-tenant applications. * The default implementation does nothing. */ void assertPermissions(); /** * Gets the object that spans this context.
* The default implementation returns null. * * @return the root object, null if in default context */ PersistentDomainObject getContextPdo(); /** * Gets the ID of the context object.
* The default implementation returns 0. * * @return the object ID, 0 if in default context */ long getContextId(); /** * Determines whether this context belongs to an inheritance hierarchy that * is created from a given context object. *

* The method is invoked from the security manager to check whether a * security rule applies to a given context. Note that contextClassId is the * class id of the context object's class. *

* The default implementation returns {@code contextId < 0 || contextId == getContextId()}.
* This is sufficient for zero or one level of context inheritance. For more than one * level this method must be overridden in each level. Example: *

   *  boolean isWithinContext(long contextId, int contextClassId) {
   *    return contextClassId == BLAH_CLASS_ID && contextId == getContextId() ||
   *           super.isWithinContext(contextId, contextClassId);
   *  }
   * 
* If the object IDs of the context objects are unique among all context entities the * contextClass can be ignored and the method reduces to: *
   *  boolean isWithinContext(long contextId, int contextClassId) {
   *    return contextId == getContextId() ||
   *           super.isWithinContext(contextId, contextClassId);
   *  }
   * 
* * @param contextId the object ID of a context object, 0 = default context * @param contextClassId the class id of the context object, 0 = default context * @return true if within that context, false if context does not apply */ boolean isWithinContext(long contextId, int contextClassId); /** * Checks whether the given name equals the name of this or one of its parent contexts.
* Notice that the empty string is always within the context, since the topmost parent context * is always unnamed. * * @param name the requested name * @return true if within context, false if not */ boolean isWithinContext(String name); /** * Returns the generic string representation of this context. *

* Use this for logging as it will not invoke methods on other objects. * * @return the String as Classname[contextId] */ String toGenericString(); /** * Get the long diagnostic string of this context.
* Used for logging, for example. * * @return the long info */ String toDiagnosticString(); /** * Creates a root context for a given root entity.
* If this context is already a root context for the same root-entity, nothing will be created * and this context returned. * * @param rootEntity the root entity * @return the root context */ DomainContext getRootContext(PersistentDomainObject rootEntity); /** * Returns whether this is a root entity context. * * @return true if root context */ boolean isRootContext(); /** * Gets a domain context which does not belong to a root entity.
* This is the original context the root-context has been cloned from. * * @return the non-root context */ DomainContext getNonRootContext(); /** * Gets the root entity for this context. * * @return the root entity, null if none */ PersistentDomainObject getRootEntity(); /** * Gets the ID of the root entity. * * @return the ID, 0 if root entity is new or there is no root entity */ long getRootId(); /** * Gets the class-ID of the root entity. * * @return the class-ID, 0 if there is no root entity */ int getRootClassId(); /** * Sets the context in a {@link DomainContextDependable}.
* The method invokes obj.setDomainContext() only if the context really differs. * This prevents infinite loops in object circular references. * * @param obj the PDO, null if ignore */ default void applyTo(DomainContextDependable obj) { if (obj instanceof PersistentDomainObject pdo) { // fast without invocation handler DomainContext otherCtx = pdo.getPersistenceDelegate().getDomainContext(); if (otherCtx != this) { pdo.getPersistenceDelegate().setDomainContext(this); } } else if (obj != null && obj.getDomainContext() != this) { obj.setDomainContext(this); } } /** * Sets the context in a list of {@link DomainContextDependable}s. * * @param list the collection of data objects */ default void applyTo(Collection list) { if (list != null) { for (DomainContextDependable obj: list) { DomainContext.this.applyTo(obj); } if (list instanceof TrackedList) { @SuppressWarnings("unchecked") Collection removedObjects = ((TrackedList) list).getRemovedObjects(); if (removedObjects != null) { for (DomainContextDependable obj: removedObjects) { DomainContext.this.applyTo(obj); } } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy