| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.internal.sessions; |
| |
| import java.util.Map; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.sessions.server.*; |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.internal.descriptors.ObjectBuilder; |
| import org.eclipse.persistence.internal.identitymaps.CacheKey; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| |
| /** |
| * Provides isolation support by allowing a client session |
| * to have a local cache of the subset of the classes. |
| * This can be used to avoid caching frequently changing data, |
| * or for security or VPD purposes. |
| */ |
| public class IsolatedClientSession extends ClientSession { |
| public IsolatedClientSession(ServerSession parent, ConnectionPolicy connectionPolicy) { |
| super(parent, connectionPolicy); |
| // PERF: Cache the write-lock check to avoid cost of checking in every register/clone. |
| this.shouldCheckWriteLock = getDatasourceLogin().shouldSynchronizedReadOnWrite() || getDatasourceLogin().shouldSynchronizeWrites(); |
| } |
| |
| public IsolatedClientSession(ServerSession parent, ConnectionPolicy connectionPolicy, Map properties) { |
| super(parent, connectionPolicy, properties); |
| // PERF: Cache the write-lock check to avoid cost of checking in every register/clone. |
| this.shouldCheckWriteLock = getDatasourceLogin().shouldSynchronizedReadOnWrite() || getDatasourceLogin().shouldSynchronizeWrites(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set up the IdentityMapManager. This method allows subclasses of Session to override |
| * the default IdentityMapManager functionality. |
| */ |
| @Override |
| public void initializeIdentityMapAccessor() { |
| this.identityMapAccessor = new IsolatedClientSessionIdentityMapAccessor(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Helper method to calculate whether to execute this query locally or send |
| * it to the server session. |
| */ |
| protected boolean shouldExecuteLocally(DatabaseQuery query) { |
| if (isIsolatedQuery(query)) { |
| return true; |
| } |
| return isInTransaction(); |
| } |
| |
| /** |
| * INTERNAL: Answers if this query is an isolated query and must be |
| * executed locally. |
| */ |
| protected boolean isIsolatedQuery(DatabaseQuery query) { |
| query.checkDescriptor(this); |
| ClassDescriptor descriptor = query.getDescriptor(); |
| if (query.isDataModifyQuery() || query.isDataReadQuery() || (descriptor != null && descriptor.getCachePolicy().isIsolated()) |
| || (query.isObjectBuildingQuery() && ((ObjectBuildingQuery)query).shouldUseExclusiveConnection())) { |
| // For CR#4334 if in transaction stay on client session. |
| // That way client's write accessor will be used for all queries. |
| // This is to preserve transaction isolation levels. |
| // also if this is an isolated class and we are in an isolated session |
| //load locally. |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the appropriate IdentityMap session for this descriptor. Sessions can be |
| * chained and each session can have its own Cache/IdentityMap. Entities can be stored |
| * at different levels based on Cache Isolation. This method will return the correct Session |
| * for a particular Entity class based on the Isolation Level and the attributes provided. |
| * |
| * @param canReturnSelf true when method calls itself. If the path |
| * starting at <code>this</code> is acceptable. Sometimes true if want to |
| * move to the first valid session, i.e. executing on ClientSession when really |
| * should be on ServerSession. |
| * @param terminalOnly return the last session in the chain where the Enitity is stored. |
| * @return Session with the required IdentityMap |
| */ |
| @Override |
| public AbstractSession getParentIdentityMapSession(ClassDescriptor descriptor, boolean canReturnSelf, boolean terminalOnly) { |
| if (canReturnSelf && (descriptor == null || descriptor.getCachePolicy().isIsolated() |
| || (descriptor.getCachePolicy().isProtectedIsolation() && !descriptor.shouldIsolateProtectedObjectsInUnitOfWork() |
| && !terminalOnly))){ |
| return this; |
| } |
| return getParent().getParentIdentityMapSession(descriptor, canReturnSelf, terminalOnly); |
| } |
| |
| /** |
| * INTERNAL: |
| * For use within the merge process this method will get an object from the shared |
| * cache using a readlock. If a readlock is unavailable then the merge manager will be |
| * transitioned to deferred locks and a deferred lock will be used. |
| */ |
| @Override |
| protected CacheKey getCacheKeyFromTargetSessionForMerge(Object implementation, ObjectBuilder builder, ClassDescriptor descriptor, MergeManager mergeManager){ |
| Object primaryKey = builder.extractPrimaryKeyFromObject(implementation, this, true); |
| CacheKey cacheKey = getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, implementation.getClass(), descriptor, true); |
| return cacheKey; |
| } |
| |
| /** |
| * INTERNAL: |
| * Gets the session which this query will be executed on. |
| * Generally will be called immediately before the call is translated, |
| * which is immediately before session.executeCall. |
| * <p> |
| * Since the execution session also knows the correct datasource platform |
| * to execute on, it is often used in the mappings where the platform is |
| * needed for type conversion, or where calls are translated. |
| * <p> |
| * Is also the session with the accessor. Will return a ClientSession if |
| * it is in transaction and has a write connection. |
| * @return a session with a live accessor |
| * @param query may store session name or reference class for brokers case |
| */ |
| @Override |
| public AbstractSession getExecutionSession(DatabaseQuery query) { |
| if (shouldExecuteLocally(query)) { |
| return this; |
| } else { |
| return this.parent.getExecutionSession(query); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if this session is an isolated client session. |
| */ |
| @Override |
| public boolean isIsolatedClientSession() { |
| return true; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns true if Protected Entities should be built within this session |
| */ |
| @Override |
| public boolean isProtectedSession(){ |
| return true; |
| } |
| |
| } |