Class JdbcTokenStore
- All Implemented Interfaces:
TokenStore
TokenStore implementation that uses JDBC to save and load TrackingToken instances.
Before using this store make sure the database contains a table named TokenSchema.tokenTable() in which to
store the tokens. For convenience, this table can be constructed through the createSchema(TokenTableFactory) operation.
- Since:
- 3.0
- Author:
- Rene de Waele
-
Nested Class Summary
Nested Classes -
Constructor Summary
ConstructorsModifierConstructorDescriptionprotectedJdbcTokenStore(JdbcTokenStore.Builder builder) Instantiate aJdbcTokenStorebased on the fields contained in theJdbcTokenStore.Builder. -
Method Summary
Modifier and TypeMethodDescriptionstatic JdbcTokenStore.Builderbuilder()Instantiate a Builder to be able to create aJdbcTokenStore.protected TrackingTokenclaimToken(Connection connection, AbstractTokenEntry<?> entry) Tries to claim the given tokenentry.voidcreateSchema(TokenTableFactory schemaFactory) Performs the DDL queries to create the schema necessary for this token store implementation.voiddeleteToken(String processorName, int segment) Deletes the token for the processor with givenprocessorNameandsegment.protected PreparedStatementdeleteToken(Connection connection, String processorName, int segment) Creates a newPreparedStatementto release the current claim this node has on a token belonging to a processor with givenprocessorNameandsegment.fetchAvailableSegments(String processorName) Returns a List of known availablesegmentsfor a givenprocessorName.int[]fetchSegments(String processorName) Returns an array of knownsegmentsfor a givenprocessorName.fetchToken(String processorName, int segment) fetchToken(String processorName, Segment segment) protected ConnectionReturns aConnectionto the database.voidinitializeSegment(TrackingToken token, String processorName, int segment) Initializes a segment with givensegmentfor the processor with givenprocessorNameto contain the giventoken.voidinitializeTokenSegments(String processorName, int segmentCount) Initializes the givensegmentCountnumber of segments for the givenprocessorNameto track its tokens.voidinitializeTokenSegments(String processorName, int segmentCount, TrackingToken initialToken) Initializes the givensegmentCountnumber of segments for the givenprocessorNameto track its tokens.protected TrackingTokeninsertTokenEntry(Connection connection, TrackingToken token, String processorName, int segment) Inserts a new token entry via the given updatableresultSet.protected TrackingTokenloadToken(Connection connection, ResultSet resultSet, String processorName, int segment) Tries loading an existing token owned by a processor with givenprocessorNameandsegment.protected TrackingTokenloadToken(Connection connection, ResultSet resultSet, String processorName, Segment segment) Tries loading an existing token owned by a processor with givenprocessorNameandsegment.protected <T> TreadSerializedData(ResultSet resultSet, String columnName) Returns the serialized token data from the givenresultSetat givencolumnName.protected AbstractTokenEntry<?> readTokenEntry(ResultSet resultSet) Convert givenresultSetto anAbstractTokenEntry.voidreleaseClaim(String processorName, int segment) Release a claim of the token for givenprocessorNameandsegment.protected PreparedStatementreleaseClaim(Connection connection, String processorName, int segment) Creates a newPreparedStatementto release the current claim this node has on a token belonging to a processor with givenprocessorNameandsegment.booleanIndicates whether this TokenStore instance requires segments to be explicitly initialized, before any tokens can be claimed for that segment.Returns a unique identifier that uniquely identifies the storage location of the tokens in this store.protected PreparedStatementselect(Connection connection, String processorName, int segment, boolean forUpdate) Returns aPreparedStatementto select a token entry from the underlying storage, either for updating or just for reading.protected PreparedStatementselectForSegments(Connection connection, String processorName) Returns aPreparedStatementto select all segments ids for a given processorName from the underlying storage.protected PreparedStatementselectForUpdate(Connection connection, String processorName, int segment) Returns aPreparedStatementto select a token entry from the underlying storage.protected PreparedStatementselectSegments(Connection connection, String processorName, int splitSegmentId, int mergeableSegmentId) Returns aPreparedStatementfor the count of segments that can be found after searching for thesplitSegmentIdandmergeableSegmentId.protected PreparedStatementselectTokenEntries(Connection connection, String processorName) Returns aPreparedStatementto select allTokenEntriesfor a given processorName from the underlying storage.Returns the serializer used by the Token Store to serialize tokens.voidstoreToken(TrackingToken token, String processorName, int segment) Stores the giventokenin the store.protected PreparedStatementstoreUpdate(Connection connection, TrackingToken token, String processorName, int segment) Returns aPreparedStatementwhich updates the giventokenfor the givenprocessorNameandsegmentcombination.protected voidupdateToken(Connection connection, ResultSet resultSet, TrackingToken token, String processorName, int segment) If the givenresultSethas an entry, attempts to replace the token in the entry with the giventokenand claim ownership.protected voidvalidateSegment(String processorName, Segment segment) Validate asegmentby checking for the existence of a split or merge candidate segment.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface org.axonframework.eventhandling.tokenstore.TokenStore
extendClaim
-
Constructor Details
-
JdbcTokenStore
Instantiate aJdbcTokenStorebased on the fields contained in theJdbcTokenStore.Builder.Will assert that the
ConnectionProvider,Serializer,TokenSchema,claimTimeout,nodeIdandcontentTypeare notnull, and will throw anAxonConfigurationExceptionif any of them isnull.- Parameters:
builder- theJdbcTokenStore.Builderused to instantiate aJdbcTokenStoreinstance
-
-
Method Details
-
builder
Instantiate a Builder to be able to create aJdbcTokenStore.The
schemais defaulted to anTokenSchema, theclaimTimeoutto a 10 seconds duration,nodeIdis defaulted to the name of the managed bean for the runtime system of the Java virtual machine and thecontentTypeto abyte[]Class. TheConnectionProviderandSerializerare hard requirements and as such should be provided.- Returns:
- a Builder to be able to create a
JdbcTokenStore
-
createSchema
Performs the DDL queries to create the schema necessary for this token store implementation.- Parameters:
schemaFactory- factory of the token entry schema
-
initializeTokenSegments
public void initializeTokenSegments(@Nonnull String processorName, int segmentCount) throws UnableToClaimTokenException Description copied from interface:TokenStoreInitializes the givensegmentCountnumber of segments for the givenprocessorNameto track its tokens. This method should only be invoked when no tokens have been stored for the given processor, yet.This method will initialize the tokens, but not claim them. It will create the segments ranging from
0untilsegmentCount - 1.The exact behavior when this method is called while tokens were already present, is undefined in case the token already present is not owned by the initializing process.
- Specified by:
initializeTokenSegmentsin interfaceTokenStore- Parameters:
processorName- The name of the processor to initialize segments forsegmentCount- The number of segments to initialize- Throws:
UnableToClaimTokenException- when a segment has already been created
-
initializeTokenSegments
public void initializeTokenSegments(@Nonnull String processorName, int segmentCount, TrackingToken initialToken) throws UnableToClaimTokenException Description copied from interface:TokenStoreInitializes the givensegmentCountnumber of segments for the givenprocessorNameto track its tokens. This method should only be invoked when no tokens have been stored for the given processor, yet.This method will store
initialTokenfor all segments as starting point for processor, but not claim them. It will create the segments ranging from0untilsegmentCount - 1.The exact behavior when this method is called while tokens were already present, is undefined in case the token already present is not owned by the initializing process.
- Specified by:
initializeTokenSegmentsin interfaceTokenStore- Parameters:
processorName- The name of the processor to initialize segments forsegmentCount- The number of segments to initializeinitialToken- The initial token which is used as a starting point for processor- Throws:
UnableToClaimTokenException- when a segment has already been created
-
initializeSegment
public void initializeSegment(@Nullable TrackingToken token, @Nonnull String processorName, int segment) throws UnableToInitializeTokenException Description copied from interface:TokenStoreInitializes a segment with givensegmentfor the processor with givenprocessorNameto contain the giventoken.This method fails if a Token already exists for the given processor and segment, even if that token has been claimed by the active instance.
This method will not claim the initialized segment. Use
TokenStore.fetchToken(String, int)to retrieve and claim the token.- Specified by:
initializeSegmentin interfaceTokenStore- Parameters:
token- The token to initialize the segment withprocessorName- The name of the processor to create the segment forsegment- The identifier of the segment to initialize- Throws:
UnableToInitializeTokenException- if a Token already exists
-
requiresExplicitSegmentInitialization
public boolean requiresExplicitSegmentInitialization()Description copied from interface:TokenStoreIndicates whether this TokenStore instance requires segments to be explicitly initialized, before any tokens can be claimed for that segment.- Specified by:
requiresExplicitSegmentInitializationin interfaceTokenStore- Returns:
trueif this instance requires tokens to be explicitly initialized, otherwisefalse.- See Also:
-
retrieveStorageIdentifier
Description copied from interface:TokenStoreReturns a unique identifier that uniquely identifies the storage location of the tokens in this store. Two token store implementations that share state, must return the same identifier. Two token store implementations that do not share a location, must return a different identifier (or an empty optional if identifiers are not supported).Note that this method may require the implementation to consult its underlying storage. Therefore, a Transaction should be active when this method is called, similarly to invocations like
TokenStore.fetchToken(String, int),TokenStore.fetchSegments(String), etc. When no Transaction is active, the behavior is undefined.- Specified by:
retrieveStorageIdentifierin interfaceTokenStore- Returns:
- an identifier to uniquely identify the storage location of tokens in this TokenStore.
- Throws:
UnableToRetrieveIdentifierException- when the implementation was unable to determine its identifier
-
serializer
Returns the serializer used by the Token Store to serialize tokens.- Returns:
- the serializer used by the Token Store to serialize tokens
-
storeToken
public void storeToken(TrackingToken token, @Nonnull String processorName, int segment) throws UnableToClaimTokenException Description copied from interface:TokenStoreStores the giventokenin the store. The token marks the current position of the process with givenprocessorNameandsegment. The giventokenmay benull. Any claims made by the current process have their timestamp updated.This method should throw an
UnableToClaimTokenExceptionwhen the givensegmenthas not been initialized with a Token (albeitnull) yet. In that case, a segment must have been explicitly initialized. A TokenStore implementation's ability to do so is exposed by theTokenStore.requiresExplicitSegmentInitialization()method. If that method returns false, this method may implicitly initialize a token and return that token upon invocation.- Specified by:
storeTokenin interfaceTokenStore- Parameters:
token- The token to store for a given process and segment. May benull.processorName- The name of the process for which to store the tokensegment- The index of the segment for which to store the token- Throws:
UnableToClaimTokenException- when the token being updated has been claimed by another process.
-
fetchToken
public TrackingToken fetchToken(@Nonnull String processorName, int segment) throws UnableToClaimTokenException Description copied from interface:TokenStoreReturns the last storedtokenfor the givenprocessorNameandsegment. Returnsnullif the stored token for the given process and segment isnull.This method should throw an
UnableToClaimTokenExceptionwhen the givensegmenthas not been initialized with a Token (albeitnull) yet. In that case, a segment must have been explicitly initialized. A TokenStore implementation's ability to do so is exposed by theTokenStore.requiresExplicitSegmentInitialization()method. If that method returns false, this method may implicitly initialize a token and return that token upon invocation.The token will be claimed by the current process (JVM instance), preventing access by other instances. To release the claim, use
TokenStore.releaseClaim(String, int)- Specified by:
fetchTokenin interfaceTokenStore- Parameters:
processorName- The process name for which to fetch the tokensegment- The segment index for which to fetch the token- Returns:
- The last stored TrackingToken or
nullif the store holds no token for given process and segment - Throws:
UnableToClaimTokenException- if there is a token for givenprocessorNameandsegment, but they are claimed by another process.
-
fetchToken
public TrackingToken fetchToken(@Nonnull String processorName, @Nonnull Segment segment) throws UnableToClaimTokenException Description copied from interface:TokenStoreReturns the last storedtokenfor the givenprocessorNameandsegment. Returnsnullif the stored token for the given process and segment isnull.This method should throw an
UnableToClaimTokenExceptionwhen the givensegmenthas not been initialized with a Token (albeitnull) yet. In that case, a segment must have been explicitly initialized. A TokenStore implementation's ability to do so is exposed by theTokenStore.requiresExplicitSegmentInitialization()method. If that method returns false, this method may implicitly initialize a token and return that token upon invocation.The token will be claimed by the current process (JVM instance), preventing access by other instances. To release the claim, use
TokenStore.releaseClaim(String, int)- Specified by:
fetchTokenin interfaceTokenStore- Parameters:
processorName- The process name for which to fetch the tokensegment- The segment for which to fetch the token- Returns:
- The last stored TrackingToken or
nullif the store holds no token for given process and segment - Throws:
UnableToClaimTokenException- if there is a token for givenprocessorNameandsegment, but they are claimed by another process, or if thesegment has been split or merged concurrently
-
releaseClaim
Description copied from interface:TokenStoreRelease a claim of the token for givenprocessorNameandsegment. If no such claim existed, nothing happens.The caller must ensure not to use any streams opened based on the token for which the claim is released.
- Specified by:
releaseClaimin interfaceTokenStore- Parameters:
processorName- The name of the process owning the token (e.g. a TrackingEventProcessor name)segment- the segment for which a token was obtained
-
deleteToken
Description copied from interface:TokenStoreDeletes the token for the processor with givenprocessorNameandsegment. The token must be owned by the current node, to be able to delete it.Implementations should implement this method only when
TokenStore.requiresExplicitSegmentInitialization()is overridden to returntrue. Deleting tokens using implementations that do not require explicit token initialization is unsafe, as a claim will automatically recreate the deleted token instance, which may result in concurrency issues.- Specified by:
deleteTokenin interfaceTokenStore- Parameters:
processorName- The name of the processor to remove the token forsegment- The segment to delete
-
fetchSegments
Description copied from interface:TokenStoreReturns an array of knownsegmentsfor a givenprocessorName.The segments returned are segments for which a token has been stored previously. When the
TokenStoreis empty, an empty array is returned.- Specified by:
fetchSegmentsin interfaceTokenStore- Parameters:
processorName- The process name for which to fetch the segments- Returns:
- an array of segment identifiers.
-
fetchAvailableSegments
Description copied from interface:TokenStoreReturns a List of known availablesegmentsfor a givenprocessorName. A segment is considered available if it is not claimed by any other event processor.The segments returned are segments for which a token has been stored previously and have not been claimed by another processor. When the
TokenStoreis empty, an empty list is returned. By default, if this method is not implemented, we will return all segments instead, whether they are available or not.- Specified by:
fetchAvailableSegmentsin interfaceTokenStore- Parameters:
processorName- the processor's name for which to fetch the segments- Returns:
- a List of available segment identifiers for the specified
processorName
-
selectForSegments
protected PreparedStatement selectForSegments(Connection connection, String processorName) throws SQLException Returns aPreparedStatementto select all segments ids for a given processorName from the underlying storage.- Parameters:
connection- the connection to the underlying databaseprocessorName- the name of the processor to fetch the segments for- Returns:
- a
PreparedStatementthat will fetch segments when executed - Throws:
SQLException- when an exception occurs while creating the prepared statement
-
selectTokenEntries
protected PreparedStatement selectTokenEntries(Connection connection, String processorName) throws SQLException Returns aPreparedStatementto select allTokenEntriesfor a given processorName from the underlying storage.- Parameters:
connection- the connection to the underlying databaseprocessorName- the name of the processor to fetch the segments for- Returns:
- a
PreparedStatementthat will fetch TokenEntries when executed - Throws:
SQLException- when an exception occurs while creating the prepared statement
-
storeUpdate
protected PreparedStatement storeUpdate(Connection connection, TrackingToken token, String processorName, int segment) throws SQLException Returns aPreparedStatementwhich updates the giventokenfor the givenprocessorNameandsegmentcombination.- Parameters:
connection- the connection to the underlying databasetoken- the new token to storeprocessorName- the name of the processor executing the updatesegment- the segment of the processor to executing the update- Returns:
- a
PreparedStatementthat will update a token entry when executed - Throws:
SQLException- when an exception occurs while creating the prepared statement
-
selectForUpdate
protected PreparedStatement selectForUpdate(Connection connection, String processorName, int segment) throws SQLException Returns aPreparedStatementto select a token entry from the underlying storage. TheResultSetthat is returned when this statement is executed should be updatable.- Parameters:
connection- the connection to the underlying databaseprocessorName- the name of the processor to fetch the entry forsegment- the segment of the processor to fetch the entry for- Returns:
- a
PreparedStatementthat will fetch an updatable token entry when executed - Throws:
SQLException- when an exception occurs while creating the prepared statement
-
select
protected PreparedStatement select(Connection connection, String processorName, int segment, boolean forUpdate) throws SQLException Returns aPreparedStatementto select a token entry from the underlying storage, either for updating or just for reading.- Parameters:
connection- the connection to the underlying databaseprocessorName- the name of the processor to fetch the entry forsegment- the segment of the processor to fetch the entry forforUpdate- whether the returned token should be updatable- Returns:
- a
PreparedStatementthat will fetch an updatable token entry when executed - Throws:
SQLException- when an exception occurs while creating the prepared statement
-
updateToken
protected void updateToken(Connection connection, ResultSet resultSet, TrackingToken token, String processorName, int segment) throws SQLException If the givenresultSethas an entry, attempts to replace the token in the entry with the giventokenand claim ownership.- Parameters:
connection- the connection to the underlying databaseresultSet- the updatable query result set of an executedPreparedStatementtoken- the token for the new or updated entryprocessorName- the name of the processor owning the tokensegment- the segment of the processor owning the token- Throws:
UnableToClaimTokenException- if the token cannot be claimed because another node currently owns the tokenSQLException- when an exception occurs while updating the result set
-
claimToken
protected TrackingToken claimToken(Connection connection, AbstractTokenEntry<?> entry) throws SQLException Tries to claim the given tokenentry. If the claim fails anUnableToClaimTokenExceptionshould be thrown. Otherwise the givenresultSetshould be updated to reflect the claim.- Parameters:
connection- the connection to the underlying databaseentry- the entry extracted from the given result set- Returns:
- the claimed tracking token
- Throws:
UnableToClaimTokenException- if the token cannot be claimed because another node currently owns the tokenSQLException- when an exception occurs while claiming the token entry
-
loadToken
protected TrackingToken loadToken(Connection connection, ResultSet resultSet, String processorName, int segment) throws SQLException Tries loading an existing token owned by a processor with givenprocessorNameandsegment. If such a token entry exists an attempt will be made to claim the token. If that succeeds the token will be returned. If the token is already owned by another node anUnableToClaimTokenExceptionwill be thrown.If no such token exists yet, a new token entry will be inserted with
nulltoken owned by this node and returnnull.- Parameters:
connection- the connection to the underlying databaseresultSet- the updatable result set from a prior select for update queryprocessorName- the name of the processor to load or insert a token entry forsegment- the segment of the processor to load or insert a token entry for- Returns:
- the tracking token of the fetched entry or
nullif a new entry was inserted - Throws:
UnableToClaimTokenException- if the token cannot be claimed because another node currently owns the tokenSQLException- when an exception occurs while loading or inserting the entry
-
loadToken
protected TrackingToken loadToken(Connection connection, ResultSet resultSet, String processorName, Segment segment) throws SQLException Tries loading an existing token owned by a processor with givenprocessorNameandsegment. If such a token entry exists an attempt will be made to claim the token. If that succeeds the token will be returned. If the token is already owned by another node anUnableToClaimTokenExceptionwill be thrown.If no such token exists yet, a new token entry will be inserted with a
nulltoken, owned by this node, and this method returnsnull.If a token has been claimed, the
segmentwill be validated by checking the database for the split and merge candidate segments. If a concurrent split or merge operation has been detected, the calim will be released and anUnableToClaimTokenExceptionwill be thrown.}- Parameters:
connection- the connection to the underlying databaseresultSet- the updatable result set from a prior select for update queryprocessorName- the name of the processor to load or insert a token entry forsegment- the segment of the processor to load or insert a token entry for- Returns:
- the tracking token of the fetched entry or
nullif a new entry was inserted - Throws:
UnableToClaimTokenException- if the token cannot be claimed because another node currently owns the token or if the segment has been split or merged concurrentlySQLException- when an exception occurs while loading or inserting the entry
-
validateSegment
Validate asegmentby checking for the existence of a split or merge candidate segment.If the segment has been split concurrently, the split segment candidate will be found, indicating that we have claimed an incorrect
segment. If the segment has been merged concurrently, the merge candidate segment will no longer exist, also indicating that we have claimed an incorrectsegment.- Parameters:
processorName- the name of the processor to load or insert a token entry forsegment- the segment of the processor to load or insert a token entry for
-
selectSegments
protected PreparedStatement selectSegments(Connection connection, String processorName, int splitSegmentId, int mergeableSegmentId) throws SQLException Returns aPreparedStatementfor the count of segments that can be found after searching for thesplitSegmentIdandmergeableSegmentId.- Parameters:
connection- the connection to the underlying databaseprocessorName- the name of the processor to load or insert a token entry forsplitSegmentId- the id of the split candidate segmentmergeableSegmentId- the id of the merge candidate segment- Returns:
- The PreparedStatement to execute
- Throws:
SQLException- when an Exception occurs in building thePreparedStatement
-
insertTokenEntry
protected TrackingToken insertTokenEntry(Connection connection, TrackingToken token, String processorName, int segment) throws SQLException Inserts a new token entry via the given updatableresultSet.- Parameters:
connection- the connection to the underlying databasetoken- the token of the entry to insertprocessorName- the name of the processor to insert a token forsegment- the segment of the processor to insert a token for- Returns:
- the tracking token of the inserted entry
- Throws:
SQLException- when an exception occurs while inserting a token entry
-
readTokenEntry
Convert givenresultSetto anAbstractTokenEntry. The result set contains a single token entry.- Parameters:
resultSet- the result set of a prior select statement containing a single token entry- Returns:
- an token entry with data extracted from the result set
- Throws:
SQLException- if the result set cannot be converted to an entry
-
releaseClaim
protected PreparedStatement releaseClaim(Connection connection, String processorName, int segment) throws SQLException Creates a newPreparedStatementto release the current claim this node has on a token belonging to a processor with givenprocessorNameandsegment.- Parameters:
connection- the connection that should be used to create aPreparedStatementprocessorName- the name of the processor for which to release this node's claimsegment- the segment of the processor for which to release this node's claim- Returns:
- a
PreparedStatementthat will release the claim this node has on the token entry - Throws:
SQLException- if the statement to release a claim cannot be created
-
deleteToken
protected PreparedStatement deleteToken(Connection connection, String processorName, int segment) throws SQLException Creates a newPreparedStatementto release the current claim this node has on a token belonging to a processor with givenprocessorNameandsegment.- Parameters:
connection- the connection that should be used to create aPreparedStatementprocessorName- the name of the processor for which to release this node's claimsegment- the segment of the processor for which to release this node's claim- Returns:
- a
PreparedStatementthat will release the claim this node has on the token entry - Throws:
SQLException- if the statement to release a claim cannot be created
-
readSerializedData
Returns the serialized token data from the givenresultSetat givencolumnName.- Type Parameters:
T- the type of data to return- Parameters:
resultSet- the result set to get serialized data fromcolumnName- the name of the column containing the serialized token- Returns:
- the serialized data of the token
- Throws:
SQLException- if the token cannot be read from the entry
-
getConnection
Returns aConnectionto the database.- Returns:
- a database Connection
-