[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