Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 1998, 2024 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.sequencing;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.MultitenantPolicy;
import org.eclipse.persistence.descriptors.SchemaPerMultitenantPolicy;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.databaseaccess.Accessor;
import org.eclipse.persistence.internal.helper.ConcurrencyManager;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.sequencing.DefaultSequence;
import org.eclipse.persistence.sequencing.Sequence;
import org.eclipse.persistence.sequencing.SequencingControl;
import org.eclipse.persistence.sessions.Login;
import org.eclipse.persistence.sessions.server.ConnectionPool;
import org.eclipse.persistence.sessions.server.ExternalConnectionPool;
import org.eclipse.persistence.sessions.server.ServerSession;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
/**
* SequencingManager is private to EclipseLink.
* It provides most of sequencing functionality.
* It's accessed by DatabaseSession through getSequencingHome() method.
*
* Here's the lifecycle of SequencingManager.
* InitialState: SequencingManager doesn't exist.
* Action: SequencingManager created -{@literal >} Not connected State.
* State: Not connected.
* isConnected() returns false;
* getSequencingControl() could be used;
* getSequencing() == getSequencingServer() == getSequencingCallbackFactory() == null;
* Action: onConnect is called -{@literal >} Connected State.
* State: Connected.
* isConnected() returns true;
* getSequencingControl() could be used;
* getSequencing() could be used;
* in case ownwerSession is a ServerSession getSequencingServer() could be used;
* Action: onDisconnect is called -{@literal >} Not connected State.
*
* Here's a sketch of SequencingManager architecture.
* The main 4 objects comprising SessionManager are:
* valueGenarationPolicy;
* preallocationHandler;
* connectionHandler;
* state;
*
* That's how they evolve during lifetime of SequencingManager object:
* Not connected State:
* preallocationHandler doesn't have any preallocated sequencing values.
* connectionHandler == null;
* state == null;
*
* Connected State:
* preallocationHandler may contain preallocated sequencing values.
* valueGenarationPolicy != null;
* state != null;
*
* The most important method of the class is onConnect():
* that's where, using values of the attributes'(accessible through SequencingControl):
* shouldUseSeparateConnection;
* login;
* minPoolSize;
* maxPoolSize;
* as well as boolean flags returned by valueGenerationPolicy methods:
* shouldAcquireValueAfterInsert();
* shouldUsePreallocation();
* shouldUseSeparateConnection();
* shouldUseTransaction();
* one of implementors of inner interface State is created.
*
* Once in Connected State, neither changes to attributes, nor to returns of valueGenerationPolicy's
* four should... methods can change the state object.
* To change the state object, onDisconnect(), than onConnect() should be called.
* There is no need to do it directly: each of the following methods
* available through SequencingControl does that:
* setValueGenerationPolicy;
* setShouldUseNativeSequencing;
* setShouldUseTableSequencing;
* resetSequencing;
*/
class SequencingManager implements SequencingHome, SequencingServer, SequencingControl {
private final DatabaseSessionImpl ownerSession;
private SequencingConnectionHandler connectionHandler;
private Map preallocationHandler;
private int whenShouldAcquireValueForAll;
private Vector connectedSequences;
boolean atLeastOneSequenceShouldUseTransaction;
boolean atLeastOneSequenceShouldUsePreallocation;
// state ids
private static final int NOPREALLOCATION = 0;
private static final int PREALLOCATION_NOTRANSACTION = 1;
private static final int PREALLOCATION_TRANSACTION_NOACCESSOR = 2;
private static final int PREALLOCATION_TRANSACTION_ACCESSOR = 3;
private static final int NUMBER_OF_STATES = 4;
private State[] states;
private Map locks;
private SequencingCallbackFactory callbackFactory;
private SequencingServer server;
private Sequencing seq;
private boolean shouldUseSeparateConnection;
private Login login;
private int minPoolSize = -1;
private int maxPoolSize = -1;
private int initialPoolSize = -1;
private ConnectionPool connectionPool;
public SequencingManager(DatabaseSessionImpl ownerSession) {
this.ownerSession = ownerSession;
}
protected DatabaseSessionImpl getOwnerSession() {
return ownerSession;
}
protected void createConnectionHandler() {
boolean isServerSession = getOwnerSession().isServerSession();
if (getLogin() == null) {
Login login;
if (isServerSession) {
login = ((ServerSession)getOwnerSession()).getReadConnectionPool().getLogin();
} else {
login = getOwnerSession().getDatasourceLogin();
}
setLogin(login);
}
if (getLogin() != null) {
if (getLogin().shouldUseExternalTransactionController()) {
throw ValidationException.invalidSequencingLogin();
}
}
if (isServerSession) {
ConnectionPool pool = null;
if (this.connectionPool == null) {
if (getLogin().shouldUseExternalConnectionPooling()) {
pool = new ExternalConnectionPool("sequencing", getLogin(), (ServerSession)getOwnerSession());
} else {
if (getMinPoolSize() == -1) {
setMinPoolSize(2);
}
if (getMaxPoolSize() == -1) {
setMinPoolSize(2);
}
if (getInitialPoolSize() == -1) {
setInitialPoolSize(1);
}
pool = new ConnectionPool("sequencing", getLogin(), getInitialPoolSize(), getMinPoolSize(), getMaxPoolSize(), (ServerSession)getOwnerSession());
}
} else {
pool = this.connectionPool;
}
setConnectionHandler(new ServerSessionConnectionHandler(pool));
} else {
setConnectionHandler(new DatabaseSessionConnectionHandler(getOwnerSession(), getLogin()));
}
}
@Override
public SequencingControl getSequencingControl() {
return this;
}
protected void setSequencing(Sequencing sequencing) {
this.seq = sequencing;
}
@Override
public Sequencing getSequencing() {
return seq;
}
protected void setSequencingServer(SequencingServer server) {
this.server = server;
}
@Override
public SequencingServer getSequencingServer() {
return server;
}
protected void setSequencingCallbackFactory(SequencingCallbackFactory callbackFactory) {
this.callbackFactory = callbackFactory;
}
@Override
public boolean isSequencingCallbackRequired() {
return this.callbackFactory != null;
}
@Override
public boolean shouldUseSeparateConnection() {
return shouldUseSeparateConnection;
}
@Override
public void setShouldUseSeparateConnection(boolean shouldUseSeparateConnection) {
this.shouldUseSeparateConnection = shouldUseSeparateConnection;
}
@Override
public boolean isConnectedUsingSeparateConnection() {
return isConnected() && (getConnectionHandler() != null);
}
@Override
public Login getLogin() {
return login;
}
@Override
public void setLogin(Login login) {
this.login = login;
}
@Override
public int getMinPoolSize() {
return minPoolSize;
}
@Override
public void setMinPoolSize(int size) {
this.minPoolSize = size;
}
@Override
public int getMaxPoolSize() {
return maxPoolSize;
}
@Override
public void setMaxPoolSize(int size) {
this.maxPoolSize = size;
}
public int getInitialPoolSize() {
return this.initialPoolSize;
}
@Override
public void setInitialPoolSize(int size) {
this.initialPoolSize = size;
}
@Override
public boolean isConnected() {
return states != null;
}
// SequencingSetup
protected SequencingConnectionHandler getConnectionHandler() {
return connectionHandler;
}
protected void setConnectionHandler(SequencingConnectionHandler handler) {
this.connectionHandler = handler;
}
@Override
public ConnectionPool getConnectionPool() {
if ((getConnectionHandler() != null) && (getConnectionHandler() instanceof ServerSessionConnectionHandler)) {
return ((ServerSessionConnectionHandler)getConnectionHandler()).getPool();
}
return this.connectionPool;
}
@Override
public Object getNextValue(Class> cls) {
return getNextValue(getOwnerSession(), cls);
}
@Override
public void initializePreallocated() {
if (preallocationHandler != null) {
for (PreallocationHandler handler : preallocationHandler.values()) {
handler.initializePreallocated();
}
}
}
@Override
public void initializePreallocated(String seqName) {
if (preallocationHandler != null) {
for (PreallocationHandler handler : preallocationHandler.values()) {
handler.initializePreallocated(seqName);
}
}
}
protected void setLocks(Map locks) {
this.locks = locks;
}
protected Map getLocks() {
return locks;
}
/**
* Acquire a lock for the sequence name.
* A lock should be, and only be, acquired when allocating new sequences from the database.
*/
protected ConcurrencyManager acquireLock(String sequenceName) {
ConcurrencyManager manager = getLocks().get(sequenceName);
if (manager == null) {
synchronized (getLocks()) {
manager = getLocks().get(sequenceName);
if (manager == null) {
manager = new ConcurrencyManager();
getLocks().put(sequenceName, manager);
}
}
}
manager.acquire();
return manager;
}
protected Sequence getSequence(Class> cls) {
//** should check here that sequencing is used?
String seqName = getOwnerSession().getDescriptor(cls).getSequenceNumberName();
return getSequence(seqName);
}
protected void logDebugPreallocation(String seqName, Object firstSequenceValue, Vector> sequences) {
if (getOwnerSession().shouldLog(SessionLog.FINEST, SessionLog.SEQUENCING)) {
// the first value has been already removed from sequences vector
Object[] args = { seqName, sequences.size() + 1, firstSequenceValue, sequences.lastElement() };
getOwnerSession().log(SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_preallocation", args);
}
}
protected void logDebugLocalPreallocation(AbstractSession writeSession, String seqName, Vector> sequences, Accessor accessor) {
if (writeSession.shouldLog(SessionLog.FINEST, SessionLog.SEQUENCING)) {
Object[] args = { seqName, sequences.size(), sequences.get(0), sequences.lastElement() };
writeSession.log(SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_localPreallocation", args, accessor);
}
}
static abstract class State {
abstract Object getNextValue(Sequence sequence, AbstractSession writeSession);
SequencingCallbackFactory getSequencingCallbackFactory() {
return null;
}
@Override
public String toString() {
String name = getClass().getName();
return name.substring(name.lastIndexOf('$') + 1);
}
}
/**
* Uses preallocation, uses transaction, no separate connection.
* This is used for a DatabaseSession, or a ServerSession not using native sequencing,
* and not using a sequence connection pool.
* This is used by default for table sequencing, unless a sequence connection pool is specified,
* however it should only be used if there is no non-JTA login available.
* This will use the writeConnection, but use individual transactions per sequence allocation,
* unless the unit of work is in an early transaction, or the connection is JTA (this may deadlock).
*/
class Preallocation_Transaction_NoAccessor_State extends State implements SequencingCallbackFactory {
final class SequencingCallbackImpl implements SequencingCallback {
Map> localSequences = new HashMap<>();
String context;
/**
* INTERNAL:
* Called after transaction has committed (commit in non-jta case; after completion - jta case).
* Should not be called after rollback.
*/
@Override
public void afterCommit(Accessor accessor) {
afterCommitInternal(context, localSequences, accessor);
}
public Map> getPreallocatedSequenceValues() {
return localSequences;
}
}
@Override
SequencingCallbackFactory getSequencingCallbackFactory() {
return this;
}
/**
* INTERNAL:
* Creates SequencingCallback.
*/
@Override
public SequencingCallback createSequencingCallback() {
return new SequencingCallbackImpl();
}
/**
* Release any locally allocated sequence back to the global sequence pool.
*/
void afterCommitInternal(String context, Map> localSequences, Accessor accessor) {
Iterator>> it = localSequences.entrySet().iterator();
while(it.hasNext()) {
Map.Entry> entry = it.next();
String seqName = entry.getKey();
Vector> localSequenceForName = entry.getValue();
if (!localSequenceForName.isEmpty()) {
getPreallocationHandler(context).setPreallocated(seqName, localSequenceForName);
// clear all localSequencesForName
localSequenceForName.clear();
}
}
if(accessor != null) {
getOwnerSession().log(SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_afterTransactionCommitted", null, accessor);
} else {
getOwnerSession().log(SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_afterTransactionCommitted", null);
}
}
SequencingCallbackImpl getCallbackImpl(AbstractSession writeSession, Accessor accessor) {
SequencingCallbackImpl seqCallbackImpl;
if(writeSession.hasExternalTransactionController()) {
// note that controller obtained from writeSession (not from ownerSession) -
// the difference is important in case of ownerSession being a member of SessionBroker:
// in that case only writeSession (which is either ClientSession or DatabaseSession) always has
// the correct controller.
seqCallbackImpl = (SequencingCallbackImpl)writeSession.getExternalTransactionController().getActiveSequencingCallback(getOwnerSession(), getSequencingCallbackFactory());
} else {
seqCallbackImpl = (SequencingCallbackImpl)accessor.getSequencingCallback(getSequencingCallbackFactory());
}
seqCallbackImpl.context = getContext(writeSession);
return seqCallbackImpl;
}
/**
* Return the next sequence value.
* First check the global pool, if empty then allocate new sequences locally.
*/
@Override
public Object getNextValue(Sequence sequence, AbstractSession writeSession) {
String seqName = sequence.getName();
if(sequence.getPreallocationSize() > 1) {
Queue