[java-opensaml] branch main updated: OSJ-342 Investigate Strategies to end of life our use of Hibernate in V5
Rod Widdowson
rdw at steadingsoftware.com
Fri Jun 17 12:55:35 UTC 2022
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch main
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=792de935f975f69fb465013bd76c229a26cf7f42
The following commit(s) were added to refs/heads/main by this push:
new 792de935f OSJ-342 Investigate Strategies to end of life our use of Hibernate in V5
792de935f is described below
commit 792de935f975f69fb465013bd76c229a26cf7f42
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Fri Jun 17 13:50:17 2022 +0100
OSJ-342 Investigate Strategies to end of life our use of Hibernate in V5
https://shibboleth.atlassian.net/browse/OSJ-342
Remove the JDBCStorageService, Record & Test.
This allows us to remove Hibernate & spring-orm as dependencies.
---
opensaml-storage-impl/pom.xml | 13 -
.../opensaml/storage/impl/JPAStorageRecord.java | 239 -------
.../opensaml/storage/impl/JPAStorageService.java | 747 ---------------------
.../storage/impl/JPAStorageServiceTest.java | 273 --------
4 files changed, 1272 deletions(-)
diff --git a/opensaml-storage-impl/pom.xml b/opensaml-storage-impl/pom.xml
index 351846e54..45980a700 100644
--- a/opensaml-storage-impl/pom.xml
+++ b/opensaml-storage-impl/pom.xml
@@ -87,19 +87,6 @@
<scope>runtime</scope>
</dependency>
- <!-- Needed for JPA storage plugin. -->
- <dependency>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-core-jakarta</artifactId>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>${spring.groupId}</groupId>
- <artifactId>spring-orm</artifactId>
- <scope>test</scope>
- <optional>true</optional>
- </dependency>
-
<!-- Test Dependencies -->
<dependency>
<groupId>${project.groupId}</groupId>
diff --git a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/JPAStorageRecord.java b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/JPAStorageRecord.java
deleted file mode 100644
index 35fadd94d..000000000
--- a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/JPAStorageRecord.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.opensaml.storage.impl;
-
-import java.io.Serializable;
-import java.util.Objects;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.storage.MutableStorageRecord;
-
-import jakarta.persistence.Column;
-import jakarta.persistence.Embeddable;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Id;
-import jakarta.persistence.IdClass;
-import jakarta.persistence.Lob;
-import jakarta.persistence.NamedQueries;
-import jakarta.persistence.NamedQuery;
-import jakarta.persistence.Table;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-
-/**
- * Implementation of {@link MutableStorageRecord} annotated for JPA.
- *
- * @param <T> type of object
- */
- at Entity
- at Table(name = "StorageRecords")
- at NamedQueries({
- @NamedQuery(name = "JPAStorageRecord.findAll",
- query = "SELECT r FROM JPAStorageRecord r"),
- @NamedQuery(name = "JPAStorageRecord.findAllContexts",
- query = "SELECT distinct r.context FROM JPAStorageRecord r"),
- @NamedQuery(name = "JPAStorageRecord.findByContext",
- query = "SELECT r FROM JPAStorageRecord r WHERE r.context = :context"),
- @NamedQuery(name = "JPAStorageRecord.updateExpirationByContext",
- query =
- "UPDATE JPAStorageRecord r SET r.expiration = :exp WHERE r.context = :context AND r.expiration >= :now"),
- @NamedQuery(name = "JPAStorageRecord.deleteByContext",
- query = "DELETE FROM JPAStorageRecord r WHERE r.context = :context"),
- @NamedQuery(name = "JPAStorageRecord.deleteByContextAndExpiration",
- query = "DELETE FROM JPAStorageRecord r WHERE r.context = :context AND r.expiration <= :exp"),
- @NamedQuery(name = "JPAStorageRecord.deleteByExpiration",
- query = "DELETE FROM JPAStorageRecord r WHERE r.expiration <= :exp")})
- at IdClass(JPAStorageRecord.RecordId.class)
-public class JPAStorageRecord<T> extends MutableStorageRecord<T> {
-
- /** Length of the context column. */
- public static final int CONTEXT_SIZE = 255;
-
- /** Length of the key column. */
- public static final int KEY_SIZE = 255;
-
- /** Context string. */
- private String context;
-
- /** Key string. */
- private String key;
-
- /**
- * Creates a new JPA storage record. All properties initialized to null.
- */
- public JPAStorageRecord() {
- super(null, null);
- }
-
- /**
- * Returns the context.
- *
- * @return context
- */
- @Id @Nonnull public String getContext() {
- return context;
- }
-
- /**
- * Sets the context.
- *
- * @param ctx to set
- */
- public void setContext(@Nonnull @NotEmpty final String ctx) {
- context = ctx;
- }
-
- /**
- * Returns the key.
- *
- * @return key
- */
- @Id @Nonnull public String getKey() {
- return key;
- }
-
- /**
- * Sets the key.
- *
- * @param k to set
- */
- public void setKey(@Nonnull @NotEmpty final String k) {
- key = k;
- }
-
- /** {@inheritDoc} */
- @Lob
- @Column(name="value", nullable = false) @Nonnull @Override public String getValue() {
- return super.getValue();
- }
-
- /** {@inheritDoc} */
- @Column(name="expires", nullable = true) @Nullable @Override public Long getExpiration() {
- return super.getExpiration();
- }
-
- /** {@inheritDoc} */
- @Column(name="version", nullable = false) @Override public long getVersion() {
- return super.getVersion();
- }
-
- /**
- * Resets the version of this storage record to 1.
- */
- public void resetVersion() {
- super.setVersion(1);
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return String.format("%s@%d::context=%s, key=%s, value=%s, expiration=%s, version=%s", getClass().getName(),
- hashCode(), context, key, getValue(), getExpiration(), getVersion());
- }
-
- /** Composite key to represent the record id. */
- @Embeddable
- public static class RecordId implements Serializable {
-
- /** serial version UID. */
- private static final long serialVersionUID = -9149627192851655684L;
-
- /** Context string. */
- private String context;
-
- /** Key string. */
- private String key;
-
- /**
- * Default constructor.
- */
- public RecordId() {
- }
-
- /**
- * Creates a new record Id.
- *
- * @param ctx context
- * @param k key
- */
- public RecordId(@Nonnull @NotEmpty final String ctx, @Nonnull @NotEmpty final String k) {
- context = ctx;
- key = k;
- }
-
- /**
- * Returns the context.
- *
- * @return context
- */
- @Column(name = "context", length = CONTEXT_SIZE, nullable = false) @Nonnull public String getContext() {
- return context;
- }
-
- /**
- * Sets the context.
- *
- * @param ctx to set
- */
- public void setContext(@Nonnull @NotEmpty final String ctx) {
- context = ctx;
- }
-
- /**
- * Returns the key.
- *
- * @return key
- */
- @Column(name="id", length = KEY_SIZE, nullable = false) @Nonnull public String getKey() {
- return key;
- }
-
- /**
- * Sets the key.
- *
- * @param k to set
- */
- public void setKey(@Nonnull @NotEmpty final String k) {
- key = k;
- }
-
- /** {@inheritDoc} */
- @Override
- public int hashCode() {
- return Objects.hash(context, key);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean equals(final Object o) {
- if (o == this) {
- return true;
- }
- if (o instanceof RecordId) {
- final RecordId id = (RecordId) o;
- return context.equals(id.context) && key.equals(id.key);
- }
- return false;
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return String.format("%s:%s", context, key);
- }
- }
-}
diff --git a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/JPAStorageService.java b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/JPAStorageService.java
deleted file mode 100644
index 55097a08d..000000000
--- a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/JPAStorageService.java
+++ /dev/null
@@ -1,747 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.opensaml.storage.impl;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TimerTask;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.storage.AbstractStorageService;
-import org.opensaml.storage.StorageCapabilitiesEx;
-import org.opensaml.storage.StorageRecord;
-import org.opensaml.storage.VersionMismatchException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import jakarta.persistence.EntityExistsException;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.EntityManagerFactory;
-import jakarta.persistence.EntityTransaction;
-import jakarta.persistence.LockModeType;
-import jakarta.persistence.Query;
-import jakarta.persistence.RollbackException;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
-import net.shibboleth.utilities.java.support.collection.Pair;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-/**
- * Implementation of {@link org.opensaml.storage.StorageService} that uses JPA to persist to a database.
- */
-public class JPAStorageService extends AbstractStorageService implements StorageCapabilitiesEx {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(JPAStorageService.class);
-
- /** Entity manager factory. */
- @Nonnull private final EntityManagerFactory entityManagerFactory;
-
- /** Number of times to retry a transaction if it rolls back. */
- @NonNegative private int transactionRetry;
-
- /**
- * Creates a new JPA storage service.
- *
- * @param factory entity manager factory
- */
- public JPAStorageService(@Nonnull final EntityManagerFactory factory) {
- entityManagerFactory = Constraint.isNotNull(factory, "EntityManagerFactory cannot be null");
-
- setContextSize(JPAStorageRecord.CONTEXT_SIZE);
- setKeySize(JPAStorageRecord.KEY_SIZE);
- setValueSize(Integer.MAX_VALUE);
- setTransactionRetry(3);
- }
-
- /**
- * Returns the number of times a transaction will be retried if a {@link RollbackException} is encountered.
- *
- * @return number of transaction retries
- */
- public int getTransactionRetry() {
- return transactionRetry;
- }
-
- /**
- * Sets the number of times a transaction will be retried (default is 3).
- *
- * @param retry number of transaction retries
- */
- public void setTransactionRetry(final int retry) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- transactionRetry =
- (int) Constraint.isGreaterThanOrEqual(0, retry,
- "Transaction retry must be greater than or equal to zero");
- }
-
- /** {@inheritDoc} */
- public boolean isServerSide() {
- return true;
- }
-
- /** {@inheritDoc} */
- public boolean isClustered() {
- return true;
- }
-
- /** {@inheritDoc} */
- @Override protected void doDestroy() {
- if (entityManagerFactory.isOpen()) {
- entityManagerFactory.close();
- }
- super.doDestroy();
- }
-
-// Checkstyle: CyclomaticComplexity|MethodLength OFF
- /** {@inheritDoc} */
- @Override public boolean create(@Nonnull @NotEmpty final String context, @Nonnull @NotEmpty final String key,
- @Nonnull @NotEmpty final String value, @Nullable @Positive final Long expiration) throws IOException {
- EntityManager manager = null;
- try {
- int retry = -1;
- RollbackException lastThrown;
- do {
- EntityTransaction transaction = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- transaction = manager.getTransaction();
- transaction.begin();
- JPAStorageRecord<?> entity =
- manager.find(JPAStorageRecord.class, new JPAStorageRecord.RecordId(context, key),
- LockModeType.PESSIMISTIC_WRITE);
- if (entity != null) {
- // Not yet expired?
- final Long exp = entity.getExpiration();
- if (exp == null || System.currentTimeMillis() < exp) {
- log.debug("Duplicate record '{}' in context '{}'", key, context);
- return false;
- }
-
- // It's dead, reset the version for merge.
- entity.resetVersion();
- } else {
- entity = new JPAStorageRecord<>();
- entity.setContext(context);
- entity.setKey(key);
- }
-
- entity.setValue(value);
- entity.setExpiration(expiration);
- manager.merge(entity);
- transaction.commit();
- log.debug("Create record '{}' in context '{}' with expiration '{}'", new Object[] {key, context,
- expiration,});
- return true;
- } catch (final EntityExistsException e) {
- rollbackTransaction(transaction);
- log.debug("Duplicate record '{}' in context '{}' with expiration '{}'", key, context, expiration);
- return false;
- } catch (final RollbackException e) {
- lastThrown = e;
- retry++;
- } catch (final Exception e) {
- rollbackTransaction(transaction);
- log.error("Error creating record '{}' in context '{}' with expiration '{}'", key, context,
- expiration, e);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- closeEntityManager(manager);
- }
- } while (retry < transactionRetry);
- throw lastThrown;
- } finally {
- closeEntityManager(manager);
- }
- }
-// Checkstyle: CyclomaticComplexity|MethodLength ON
-
- /**
- * Returns all records from the store.
- *
- * @return all records or an empty list
- * @throws IOException if errors occur in the read process
- */
- @Nonnull @NonnullElements public List<?> readAll() throws IOException {
- EntityManager manager = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- return executeNamedQuery(manager, "JPAStorageRecord.findAll", null, StorageRecord.class,
- LockModeType.PESSIMISTIC_READ);
- } finally {
- closeEntityManager(manager);
- }
- }
-
- /**
- * Returns all records from the store for the supplied context.
- *
- * @param context a storage context label
- *
- * @return all records in the context or an empty list
- * @throws IOException if errors occur in the read process
- */
- @Nonnull @NonnullElements public List<?> readAll(@Nonnull @NotEmpty final String context)
- throws IOException {
- EntityManager manager = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- final Map<String, Object> params = new HashMap<>();
- params.put("context", context);
- return executeNamedQuery(manager, "JPAStorageRecord.findByContext", params, StorageRecord.class,
- LockModeType.PESSIMISTIC_READ);
- } finally {
- closeEntityManager(manager);
- }
- }
-
- /**
- * Returns all contexts from the store.
- *
- * @return all contexts or an empty list
- * @throws IOException if errors occur in the read process
- */
- @Nonnull @NonnullElements public List<String> readContexts() throws IOException {
- EntityManager manager = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- // this query uses the distinct keyword, it must use optimistic locking
- return executeNamedQuery(manager, "JPAStorageRecord.findAllContexts", null, String.class,
- LockModeType.OPTIMISTIC);
- } finally {
- closeEntityManager(manager);
- }
- }
-
- /** {@inheritDoc} */
- @Override @Nullable public <T> StorageRecord<T> read(@Nonnull @NotEmpty final String context,
- @Nonnull @NotEmpty final String key) throws IOException {
- return this.<T>readImpl(context, key, null).getSecond();
- }
-
- /** {@inheritDoc} */
- @Override @Nonnull public <T> Pair<Long, StorageRecord<T>> read(@Nonnull @NotEmpty final String context,
- @Nonnull @NotEmpty final String key, @Positive final long version) throws IOException {
- return readImpl(context, key, version);
- }
-
- // Checkstyle: CyclomaticComplexity OFF
- /**
- * Reads the record matching the supplied parameters. Returns an empty pair if the record cannot be found or is
- * expired.
- *
- * @param <T> type of object
- * @param context to search for
- * @param key to search for
- * @param version to match
- *
- * @return pair of version and storage record
- * @throws IOException if errors occur in the read process
- */
- @Nonnull protected <T> Pair<Long, StorageRecord<T>> readImpl(@Nonnull @NotEmpty final String context,
- @Nonnull @NotEmpty final String key, @Positive final Long version) throws IOException {
- EntityManager manager = null;
- EntityTransaction transaction = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- transaction = manager.getTransaction();
- transaction.begin();
- final JPAStorageRecord<T> entity =
- manager.find(JPAStorageRecord.class, new JPAStorageRecord.RecordId(context, key),
- LockModeType.PESSIMISTIC_READ);
- if (entity == null) {
- log.debug("Read failed, key '{}' not found in context '{}'", key, context);
- return new Pair<>();
- }
- final Long exp = entity.getExpiration();
- if (exp != null && System.currentTimeMillis() >= exp) {
- log.debug("Read failed, key '{}' expired in context '{}'", key, context);
- return new Pair<>();
- }
- if (version != null && entity.getVersion() == version) {
- // Nothing's changed, so just echo back the version.
- return new Pair<>(version, null);
- }
- return new Pair<>(entity.getVersion(), entity);
- } catch (final Exception e) {
- log.error("Error reading record '{}' in context '{}'", key, context, e);
- rollbackTransaction(transaction);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- closeEntityManager(manager);
- }
- }
-
- // Checkstyle: CyclomaticComplexity ON
-
- /** {@inheritDoc} */
- @Override public boolean update(@Nonnull @NotEmpty final String context, @Nonnull @NotEmpty final String key,
- @Nonnull @NotEmpty final String value, @Nullable @Positive final Long expiration) throws IOException {
- try {
- return updateImpl(null, context, key, value, expiration) != null;
- } catch (final VersionMismatchException e) {
- throw new IllegalStateException("Unexpected exception thrown by update.", e);
- }
- }
-
- /** {@inheritDoc} */
- @Override @Nullable public Long updateWithVersion(@Positive final long version,
- @Nonnull @NotEmpty final String context, @Nonnull @NotEmpty final String key,
- @Nonnull @NotEmpty final String value, @Nullable @Positive final Long expiration) throws IOException,
- VersionMismatchException {
- return updateImpl(version, context, key, value, expiration);
- }
-
- /** {@inheritDoc} */
- @Override public boolean updateExpiration(@Nonnull @NotEmpty final String context,
- @Nonnull @NotEmpty final String key, @Nullable @Positive final Long expiration) throws IOException {
- try {
- return updateImpl(null, context, key, null, expiration) != null;
- } catch (final VersionMismatchException e) {
- throw new IllegalStateException("Unexpected exception thrown by update.", e);
- }
- }
-
- // Checkstyle: MethodLength OFF
- // Checkstyle: CyclomaticComplexity OFF
- /**
- * Updates the record matching the supplied parameters. Returns null if the record cannot be found or is expired.
- *
- * @param version to check
- * @param context to search for
- * @param key to search for
- * @param value to update
- * @param expiration to update
- *
- * @return whether the record was updated
- * @throws IOException if errors occur in the update process
- * @throws VersionMismatchException if the record found contains a version that does not match the parameter
- */
- @Nullable protected Long updateImpl(@Nullable final Long version, @Nonnull @NotEmpty final String context,
- @Nonnull @NotEmpty final String key, @Nonnull @NotEmpty final String value,
- @Nullable @Positive final Long expiration) throws IOException, VersionMismatchException {
- EntityManager manager = null;
- try {
- int retry = -1;
- RollbackException lastThrown;
- do {
- EntityTransaction transaction = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- transaction = manager.getTransaction();
- transaction.begin();
- final JPAStorageRecord<?> entity =
- manager.find(JPAStorageRecord.class, new JPAStorageRecord.RecordId(context, key),
- LockModeType.PESSIMISTIC_WRITE);
- if (entity == null) {
- log.debug("Update failed, key '{}' not found in context '{}'", key, context);
- return null;
- }
-
- final Long exp = entity.getExpiration();
- if (exp != null && System.currentTimeMillis() >= exp) {
- log.debug("Update failed, key '{}' expired in context '{}'", key, context);
- return null;
- }
-
- if (version != null && entity.getVersion() != version) {
- // Caller is out of sync.
- throw new VersionMismatchException();
- }
-
- if (value != null) {
- entity.setValue(value);
- entity.incrementVersion();
- }
- entity.setExpiration(expiration);
- manager.merge(entity);
- transaction.commit();
- log.debug("Update record '{}' in context '{}' with expiration '{}'", new Object[] {key, context,
- expiration,});
- return entity.getVersion();
- } catch (final VersionMismatchException e) {
- throw e;
- } catch (final RollbackException e) {
- lastThrown = e;
- retry++;
- } catch (final Exception e) {
- log.error("Error updating record '{}' in context '{}'", key, context, e);
- rollbackTransaction(transaction);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- closeEntityManager(manager);
- }
- } while (retry < transactionRetry);
- throw lastThrown;
- } finally {
- closeEntityManager(manager);
- }
- }
-
- // Checkstyle: CyclomaticComplexity ON
- // Checkstyle: MethodLength ON
-
- /** {@inheritDoc} */
- @Override public boolean deleteWithVersion(@Positive final long version, @Nonnull @NotEmpty final String context,
- @Nonnull @NotEmpty final String key) throws IOException, VersionMismatchException {
- return deleteImpl(version, context, key);
- }
-
- /** {@inheritDoc} */
- @Override public boolean delete(@Nonnull @NotEmpty final String context, @Nonnull @NotEmpty final String key)
- throws IOException {
- try {
- return deleteImpl(null, context, key);
- } catch (final VersionMismatchException e) {
- throw new IllegalStateException("Unexpected exception thrown by delete.", e);
- }
- }
-
- // Checkstyle: CyclomaticComplexity OFF
- /**
- * Deletes the record matching the supplied parameters.
- *
- * @param version to check
- * @param context to search for
- * @param key to search for
- *
- * @return whether the record was deleted
- * @throws IOException if errors occur in the delete process
- * @throws VersionMismatchException if the record found contains a version that does not match the parameter
- */
- protected boolean deleteImpl(@Nullable @Positive final Long version, @Nonnull @NotEmpty final String context,
- @Nonnull @NotEmpty final String key) throws IOException, VersionMismatchException {
- EntityManager manager = null;
- try {
- int retry = -1;
- RollbackException lastThrown;
- do {
- EntityTransaction transaction = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- transaction = manager.getTransaction();
- transaction.begin();
- final JPAStorageRecord<?> entity =
- manager.find(JPAStorageRecord.class, new JPAStorageRecord.RecordId(context, key),
- LockModeType.PESSIMISTIC_WRITE);
- if (entity == null) {
- log.debug("Deleting record '{}' in context '{}'....key not found", key, context);
- return false;
- } else if (version != null && entity.getVersion() != version) {
- throw new VersionMismatchException();
- } else {
- manager.remove(entity);
- transaction.commit();
- log.debug("Deleted record '{}' in context '{}'", key, context);
- return true;
- }
- } catch (final VersionMismatchException e) {
- throw e;
- } catch (final RollbackException e) {
- lastThrown = e;
- retry++;
- } catch (final Exception e) {
- log.error("Error deleting record '{}' in context '{}'", key, context, e);
- rollbackTransaction(transaction);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- closeEntityManager(manager);
- }
- } while (retry < transactionRetry);
- throw lastThrown;
- } finally {
- closeEntityManager(manager);
- }
- }
-
- // Checkstyle: CyclomaticComplexity ON
-
- // Checkstyle: CyclomaticComplexity OFF
- /** {@inheritDoc} */
- @Override public void updateContextExpiration(@Nonnull @NotEmpty final String context,
- @Nullable @Positive final Long expiration) throws IOException {
- EntityManager manager = null;
- try {
- int retry = -1;
- RollbackException lastThrown;
- do {
- EntityTransaction transaction = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- transaction = manager.getTransaction();
- transaction.begin();
- // cannot set lock mode on a non-select query
- final Query queryResults = manager.createNamedQuery("JPAStorageRecord.updateExpirationByContext");
- queryResults.setParameter("context", context);
- queryResults.setParameter("now", System.currentTimeMillis());
- queryResults.setParameter("exp", expiration);
- final int count = queryResults.executeUpdate();
- transaction.commit();
- log.debug("Updated expiration of {} record(s) in context '{}' to '{}'", count, context, expiration);
- return;
- } catch (final RollbackException e) {
- lastThrown = e;
- retry++;
- } catch (final Exception e) {
- log.error("Error updating context expiration in context '{}'", context, e);
- rollbackTransaction(transaction);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- closeEntityManager(manager);
- }
- } while (retry < transactionRetry);
- throw lastThrown;
- } finally {
- closeEntityManager(manager);
- }
- }
-
- // Checkstyle: CyclomaticComplexity ON
-
- /** {@inheritDoc} */
- @Override public void deleteContext(@Nonnull @NotEmpty final String context) throws IOException {
- deleteContextImpl(context, null);
- log.debug("Deleted all entities in context '{}'", context);
- }
-
- /** {@inheritDoc} */
- @Override public void reap(@Nonnull @NotEmpty final String context) throws IOException {
- deleteContextImpl(context, System.currentTimeMillis());
- log.debug("Reaped all entities in context '{}'", context);
- }
-
- // Checkstyle: CyclomaticComplexity OFF
- /**
- * Deletes every record with the supplied context. If expiration is supplied, only records with an expiration before
- * the supplied expiration will be removed.
- *
- * @param context to delete
- * @param expiration (optional) to require for deletion
- *
- * @throws IOException if errors occur in the delete process
- */
- protected void deleteContextImpl(@Nonnull @NotEmpty final String context, @Nonnull final Long expiration)
- throws IOException {
- EntityManager manager = null;
- try {
- int retry = -1;
- RollbackException lastThrown;
- do {
- EntityTransaction transaction = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- transaction = manager.getTransaction();
- transaction.begin();
- // cannot set lock mode on a non-select query
- final Query queryResults;
- if (expiration == null) {
- queryResults = manager.createNamedQuery("JPAStorageRecord.deleteByContext");
- } else {
- queryResults = manager.createNamedQuery("JPAStorageRecord.deleteByContextAndExpiration");
- queryResults.setParameter("exp", expiration);
- }
- queryResults.setParameter("context", context);
- final int count = queryResults.executeUpdate();
- transaction.commit();
- log.debug("Deleted {} record(s) in context '{}' with expiration '{}'", count, context, expiration);
- return;
- } catch (final RollbackException e) {
- lastThrown = e;
- retry++;
- } catch (final Exception e) {
- log.error("Error deleting context '{}'", context, e);
- rollbackTransaction(transaction);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- closeEntityManager(manager);
- }
- } while (retry < transactionRetry);
- throw lastThrown;
- } finally {
- closeEntityManager(manager);
- }
- }
-
- // Checkstyle: CyclomaticComplexity ON
-
- // Checkstyle: CyclomaticComplexity OFF
- /**
- * Deletes every record with an expiration before the supplied expiration.
- *
- * @param expiration of records to delete
- *
- * @throws IOException if errors occur in the cleanup process
- */
- protected void deleteImpl(@Nonnull final Long expiration) throws IOException {
- EntityManager manager = null;
- try {
- int retry = -1;
- RollbackException lastThrown;
- do {
- EntityTransaction transaction = null;
- try {
- manager = entityManagerFactory.createEntityManager();
- transaction = manager.getTransaction();
- transaction.begin();
- // cannot set lock mode on a non-select query
- final Query queryResults = manager.createNamedQuery("JPAStorageRecord.deleteByExpiration");
- queryResults.setParameter("exp", expiration);
- final int count = queryResults.executeUpdate();
- transaction.commit();
- log.debug("Deleted {} record(s) with expiration '{}'", count, expiration);
- return;
- } catch (final RollbackException e) {
- lastThrown = e;
- retry++;
- } catch (final Exception e) {
- log.error("Error deleting with expiration '{}'", expiration, e);
- rollbackTransaction(transaction);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- closeEntityManager(manager);
- }
- } while (retry < transactionRetry);
- throw lastThrown;
- } finally {
- closeEntityManager(manager);
- }
- }
-
- // Checkstyle: CyclomaticComplexity ON
-
- // Checkstyle: CyclomaticComplexity OFF
- /**
- * Executes the supplied named query.
- *
- * @param <T> type of entity to return
- * @param manager to execute the query
- * @param query to execute
- * @param params parameters for the query
- * @param clazz type of entity to return
- * @param lockMode of the transaction
- *
- * @return query results or an empty list
- * @throws IOException if an error occurs executing the query
- */
- private <T> List<T> executeNamedQuery(@Nonnull final EntityManager manager, @Nonnull @NotEmpty final String query,
- @Nonnull final Map<String, Object> params, @Nonnull final Class<T> clazz,
- @Nonnull final LockModeType lockMode) throws IOException {
- final List<T> results = new ArrayList<>();
- EntityTransaction transaction = null;
- try {
- transaction = manager.getTransaction();
- transaction.begin();
- final Query queryResults = manager.createNamedQuery(query, clazz);
- queryResults.setLockMode(lockMode);
- if (params != null && !params.isEmpty()) {
- for (final Map.Entry<String, Object> entry : params.entrySet()) {
- queryResults.setParameter(entry.getKey(), entry.getValue());
- }
- }
- results.addAll(queryResults.getResultList());
- } catch (final Exception e) {
- log.error("Error executing named query", e);
- rollbackTransaction(transaction);
- throw new IOException(e);
- } finally {
- commitTransaction(transaction);
- }
- return results;
- }
-
- // Checkstyle: CyclomaticComplexity ON
-
- /** {@inheritDoc} */
- @Override @Nullable protected TimerTask getCleanupTask() {
- return new TimerTask() {
-
- /** {@inheritDoc} */
- @Override public void run() {
- final Long now = System.currentTimeMillis();
- log.debug("Running cleanup task at {}", now);
- try {
- deleteImpl(now);
- } catch (final IOException e) {
- log.error("Error running cleanup task for {}", now, e);
- }
- log.debug("Finished cleanup task for {}", now);
- }
- };
- }
-
- /**
- * Commits the supplied transaction if {@link EntityTransaction#isActive()} and not {@link
- * EntityTransaction#getRollbackOnly()}. Logs any exception that occurs.
- *
- * @param transaction to commit
- */
- private void commitTransaction(@Nullable final EntityTransaction transaction) {
- if (transaction != null && transaction.isActive() && !transaction.getRollbackOnly()) {
- try {
- transaction.commit();
- } catch (final Exception e) {
- log.error("Error committing transaction", e);
- }
- }
- }
-
- /**
- * Rolls back the supplied transaction if {@link EntityTransaction#isActive()}. Logs any exception that occurs.
- *
- * @param transaction to roll back
- */
- private void rollbackTransaction(@Nullable final EntityTransaction transaction) {
- if (transaction != null && transaction.isActive()) {
- try {
- transaction.rollback();
- } catch (final Exception e) {
- log.error("Error rolling back transaction", e);
- }
- }
- }
-
- /**
- * Closes the supplied entity manager if {@link EntityManager#isOpen()}. Logs any exception that occurs.
- *
- * @param manager to close
- */
- private void closeEntityManager(@Nullable final EntityManager manager) {
- if (manager != null && manager.isOpen()) {
- try {
- manager.close();
- } catch (final Exception e) {
- log.error("Error closing entity manager", e);
- }
- }
- }
-}
diff --git a/opensaml-storage-impl/src/test/java/org/opensaml/storage/impl/JPAStorageServiceTest.java b/opensaml-storage-impl/src/test/java/org/opensaml/storage/impl/JPAStorageServiceTest.java
deleted file mode 100644
index 30daafb23..000000000
--- a/opensaml-storage-impl/src/test/java/org/opensaml/storage/impl/JPAStorageServiceTest.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.opensaml.storage.impl;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-import java.time.Duration;
-import java.util.List;
-import java.util.UUID;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.storage.StorageRecord;
-import org.opensaml.storage.StorageService;
-import org.opensaml.storage.testing.StorageServiceTest;
-import org.springframework.beans.factory.FactoryBean;
-import org.springframework.context.support.GenericApplicationContext;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import jakarta.persistence.EntityManagerFactory;
-import net.shibboleth.ext.spring.util.ApplicationContextBuilder;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-
-/**
- * Test of {@link JPAStorageService} implementation.
- */
-public class JPAStorageServiceTest extends StorageServiceTest {
-
- /** Storage service. */
- private JPAStorageService storageService;
-
- /** Contexts used for testing. */
- private Object[][] contexts;
-
- public JPAStorageServiceTest() {
- final SecureRandom random1 = new SecureRandom();
- contexts = new Object[10][1];
- for (int i = 0; i < 10; i++) {
- contexts[i] = new Object[] {Long.toString(random1.nextLong()), };
- }
- }
-
- /**
- * Creates the shared instance of the entity manager factory.
- */
- @BeforeClass public void setUp() throws ComponentInitializationException {
- storageService = new JPAStorageService(createEntityManagerFactory());
- storageService.setId("test");
- storageService.setCleanupInterval(Duration.ofSeconds(5));
- storageService.setTransactionRetry(2);
- super.setUp();
- }
-
- /**
- * Creates an entity manager factory instance.
- *
- * @return an entity manager factory instance
- *
- * @throws ComponentInitializationException ...
- */
- private EntityManagerFactory createEntityManagerFactory() throws ComponentInitializationException
- {
- final Resource resource = new ClassPathResource("/org/opensaml/storage/impl/jpa-spring-context.xml");
- final GenericApplicationContext context =
- new ApplicationContextBuilder()
- .setName("JPAStorageService")
- .setServiceConfiguration(resource)
- .build();
- final FactoryBean<EntityManagerFactory> factoryBean =
- context.getBean(org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.class);
- try {
- return factoryBean.getObject();
- } catch (Exception e) {
- throw new ComponentInitializationException(e);
- }
- }
-
- @AfterClass
- protected void tearDown() {
- try {
- List<String> contexts1 = storageService.readContexts();
- for (String ctx : contexts1) {
- storageService.deleteContext(ctx);
- }
- List<?> recs = storageService.readAll();
- Assert.assertEquals(recs.size(), 0);
- } catch (IOException e){
- throw new RuntimeException(e);
- }
- super.tearDown();
- }
-
- @Nonnull protected StorageService getStorageService() {
- return storageService;
- }
-
- @Test
- public void cleanup() throws ComponentInitializationException, IOException {
- String context = Long.toString(random.nextLong());
- for (int i = 1; i <= 100; i++) {
- storageService.create(context, Integer.toString(i), Integer.toString(i + 1), System.currentTimeMillis() + 100);
- }
- try {
- Thread.sleep(7500);
- } catch (InterruptedException e) {
- throw new IOException(e);
- }
- List<?> recs = storageService.readAll(context);
- Assert.assertEquals(recs.size(), 0);
- }
-
- @DataProvider(name = "contexts")
- public Object[][] contexts() throws Exception {
- return contexts;
- }
-
- @Test(dataProvider = "contexts", singleThreaded = false, threadPoolSize = 25, invocationCount = 100)
- public void multithread(final String context) throws IOException {
- shared.create(context, "mt", "bar", System.currentTimeMillis() + 300000);
- StorageRecord<?> rec = shared.read(context, "mt");
- Assert.assertNotNull(rec);
- shared.update(context, "mt", "baz", System.currentTimeMillis() + 300000);
- rec = shared.read(context, "mt");
- Assert.assertNotNull(rec);
- boolean result = shared.create(context, "mt", "qux", null);
- Assert.assertFalse(result, "createString should have failed");
- }
-
- @Test(singleThreaded = false, threadPoolSize = 25, invocationCount = 100)
- public void multithreadCaseSensitiveKey() throws IOException {
- shared.create("unit_test", "foo", "bar", null);
- shared.create("unit_test", "FOO", "bar", null);
- StorageRecord<?> rec1 = shared.read("unit_test", "foo");
- StorageRecord<?> rec2 = shared.read("unit_test", "FOO");
- Assert.assertNotNull(rec1);
- Assert.assertNotNull(rec2);
- Assert.assertNotEquals(rec1, rec2);
- }
-
- @Test
- public void keyCollision() throws IOException {
- shared.create("unit_test", "dlo1", "value", null);
- shared.create("unit_test", "dn11", "value", null);
- StorageRecord<?> rec1 = shared.read("unit_test", "dlo1");
- StorageRecord<?> rec2 = shared.read("unit_test", "dn11");
- Assert.assertNotNull(rec1);
- Assert.assertNotNull(rec2);
- Assert.assertNotEquals(rec1, rec2);
-
- shared.update("unit_test", "dlo1", "value2", null);
- shared.update("unit_test", "dn11", "value2", null);
- rec1 = shared.read("unit_test", "dlo1");
- rec2 = shared.read("unit_test", "dn11");
- Assert.assertNotNull(rec1);
- Assert.assertNotNull(rec2);
- Assert.assertNotEquals(rec1, rec2);
-
- Assert.assertEquals(2, storageService.readAll().size());
- Assert.assertEquals(2, storageService.readAll("unit_test").size());
-
- shared.delete("unit_test", "dlo1");
- rec1 = shared.read("unit_test", "dlo1");
- rec2 = shared.read("unit_test", "dn11");
- Assert.assertNull(rec1);
- Assert.assertNotNull(rec2);
- shared.delete("unit_test", "dn11");
- rec1 = shared.read("unit_test", "dlo1");
- rec2 = shared.read("unit_test", "dn11");
- Assert.assertNull(rec1);
- Assert.assertNull(rec2);
- }
-
- @Test
- public void caseSensitiveContext() throws IOException {
- shared.create("foo", "bar", "value", null);
- shared.create("FOO", "bar", "value", null);
- StorageRecord<?> rec1 = shared.read("foo", "bar");
- StorageRecord<?> rec2 = shared.read("FOO", "bar");
- Assert.assertNotNull(rec1);
- Assert.assertNotNull(rec2);
- Assert.assertNotEquals(rec1, rec2);
-
- shared.update("foo", "bar", "value2", null);
- shared.update("FOO", "bar", "value2", null);
- rec1 = shared.read("foo", "bar");
- rec2 = shared.read("FOO", "bar");
- Assert.assertNotNull(rec1);
- Assert.assertNotNull(rec2);
- Assert.assertNotEquals(rec1, rec2);
-
- Assert.assertEquals(2, storageService.readAll().size());
- Assert.assertEquals(1, storageService.readAll("foo").size());
- Assert.assertEquals(1, storageService.readAll("FOO").size());
-
- shared.delete("foo", "bar");
- rec1 = shared.read("foo", "bar");
- rec2 = shared.read("FOO", "bar");
- Assert.assertNull(rec1);
- Assert.assertNotNull(rec2);
- shared.delete("FOO", "bar");
- rec1 = shared.read("foo", "bar");
- rec2 = shared.read("FOO", "bar");
- Assert.assertNull(rec1);
- Assert.assertNull(rec2);
- }
-
- @Test
- public void caseSensitiveKey() throws IOException {
- shared.create("unit_test", "foo", "value", null);
- shared.create("unit_test", "FOO", "value", null);
- StorageRecord<?> rec1 = shared.read("unit_test", "foo");
- StorageRecord<?> rec2 = shared.read("unit_test", "FOO");
- Assert.assertNotNull(rec1);
- Assert.assertNotNull(rec2);
- Assert.assertNotEquals(rec1, rec2);
-
- shared.update("unit_test", "foo", "value2", null);
- shared.update("unit_test", "FOO", "value2", null);
- rec1 = shared.read("unit_test", "foo");
- rec2 = shared.read("unit_test", "FOO");
- Assert.assertNotNull(rec1);
- Assert.assertNotNull(rec2);
- Assert.assertNotEquals(rec1, rec2);
-
- Assert.assertEquals(2, storageService.readAll().size());
- Assert.assertEquals(2, storageService.readAll("unit_test").size());
-
- shared.delete("unit_test", "foo");
- rec1 = shared.read("unit_test", "foo");
- rec2 = shared.read("unit_test", "FOO");
- Assert.assertNull(rec1);
- Assert.assertNotNull(rec2);
- shared.delete("unit_test", "FOO");
- rec1 = shared.read("unit_test", "foo");
- rec2 = shared.read("unit_test", "FOO");
- Assert.assertNull(rec1);
- Assert.assertNull(rec2);
- }
-
- @Test(enabled = false)
- public void largeValue() throws IOException {
- // hsqldb defaults LOB length to 255 chars; disabled for now
- StringBuilder sb = new StringBuilder(1000 * 36);
- for (int i = 0; i < 1000; i++) {
- sb.append(UUID.randomUUID());
- }
- shared.create("unit_test", "large", sb.toString(), System.currentTimeMillis() + 300000);
- StorageRecord<?> rec = shared.read("unit_test", "large");
- Assert.assertNotNull(rec);
- Assert.assertEquals(sb.toString(), rec.getValue());
- }
-}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list