diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java index f8fbff089e..d7ca658485 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java @@ -475,22 +475,22 @@ public void unlock() { @Override public void createPersistenceStructure(boolean createSchemas) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("This BrokerFactory does not implement schema creation."); } @Override public void dropPersistenceStructure(boolean dropSchemas) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("This BrokerFactory does not implement schema dropping."); } @Override public void validatePersistenceStructure() throws Exception { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("This BrokerFactory does not implement schema validation."); } @Override public void truncateData() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("This BrokerFactory does not implement data truncation."); } /** diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/SchemaManagerImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/SchemaManagerImpl.java index bbf6c78c84..2d61446a2f 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/SchemaManagerImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/SchemaManagerImpl.java @@ -25,7 +25,7 @@ /** * Implements a no-op SchemaManager object that will throw - * UnsupportedOperationException if not concretelly implemented + * UnsupportedOperationException if not concretely implemented * by the given persistence layer. * * @author Paulo Cristovão Filho @@ -52,15 +52,23 @@ public void drop(boolean dropSchemas) { public void validate() throws SchemaValidationException { try { _factory.validatePersistenceStructure(); + } catch (UnsupportedOperationException uoe) { + // the store does not implement schema validation at all. That is a missing capability, + // not a validation failure, so report it the same way create(), drop() and truncate() do. + throw uoe; } catch (Exception ex) { throw new SchemaValidationException( - String.format("Schema could not be validated: %s", ex.getLocalizedMessage()), - (Exception) ex); + String.format("Schema could not be validated: %s", describe(ex)), ex); } } + private static String describe(Exception ex) { + String message = ex.getLocalizedMessage(); + return message != null ? message : ex.getClass().getName(); + } + @Override public void truncate() { - _factory.truncateData();; + _factory.truncateData(); } } diff --git a/openjpa-persistence/src/test/java/org/apache/openjpa/persistence/TestSchemaManagerImpl.java b/openjpa-persistence/src/test/java/org/apache/openjpa/persistence/TestSchemaManagerImpl.java new file mode 100644 index 0000000000..2e9444a01f --- /dev/null +++ b/openjpa-persistence/src/test/java/org/apache/openjpa/persistence/TestSchemaManagerImpl.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.openjpa.persistence; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import org.apache.openjpa.conf.OpenJPAConfigurationImpl; +import org.apache.openjpa.kernel.AbstractBrokerFactory; +import org.apache.openjpa.kernel.StoreManager; +import org.junit.Test; + +import jakarta.persistence.SchemaManager; +import jakarta.persistence.SchemaValidationException; + +/** + * Tests that {@link SchemaManagerImpl} reports a store which does not implement the + * schema operations as an unsupported operation rather than as a schema failure. + */ +public class TestSchemaManagerImpl { + + /** + * A minimal, non-JDBC {@link AbstractBrokerFactory} which inherits the default + * (unsupported) implementations of the four schema operations. + */ + private static final class NoSchemaBrokerFactory extends AbstractBrokerFactory { + private static final long serialVersionUID = 1L; + + private NoSchemaBrokerFactory() { + super(new OpenJPAConfigurationImpl()); + } + + @Override + protected StoreManager newStoreManager() { + return null; + } + } + + /** + * A factory whose validation fails the way a real store reports schema drift. + */ + private static final class FailingBrokerFactory extends AbstractBrokerFactory { + private static final long serialVersionUID = 1L; + + static final IllegalStateException FAILURE = new IllegalStateException("column FOO is missing"); + + private FailingBrokerFactory() { + super(new OpenJPAConfigurationImpl()); + } + + @Override + public void validatePersistenceStructure() throws Exception { + throw FAILURE; + } + + @Override + protected StoreManager newStoreManager() { + return null; + } + } + + /** + * A factory whose validation fails without carrying any message at all. + */ + private static final class SilentlyFailingBrokerFactory extends AbstractBrokerFactory { + private static final long serialVersionUID = 1L; + + private SilentlyFailingBrokerFactory() { + super(new OpenJPAConfigurationImpl()); + } + + @Override + public void validatePersistenceStructure() throws Exception { + throw new IllegalStateException(); + } + + @Override + protected StoreManager newStoreManager() { + return null; + } + } + + @Test + public void testValidateReportsUnsupportedOperationInsteadOfValidationFailure() { + SchemaManager schemaManager = new SchemaManagerImpl(new NoSchemaBrokerFactory()); + + UnsupportedOperationException uoe = + assertThrows(UnsupportedOperationException.class, schemaManager::validate); + assertNotNull("the unsupported operation must carry a message", uoe.getMessage()); + assertTrue(uoe.getMessage(), uoe.getMessage().contains("schema validation")); + } + + @Test + public void testCreateDropAndTruncateReportUnsupportedOperation() { + SchemaManager schemaManager = new SchemaManagerImpl(new NoSchemaBrokerFactory()); + + assertThrows(UnsupportedOperationException.class, () -> schemaManager.create(true)); + assertThrows(UnsupportedOperationException.class, () -> schemaManager.drop(true)); + assertThrows(UnsupportedOperationException.class, schemaManager::truncate); + } + + @Test + public void testValidateWrapsRealValidationFailures() { + SchemaManager schemaManager = new SchemaManagerImpl(new FailingBrokerFactory()); + + SchemaValidationException sve = + assertThrows(SchemaValidationException.class, schemaManager::validate); + assertEquals("Schema could not be validated: column FOO is missing", sve.getMessage()); + assertEquals(1, sve.getFailures().length); + assertSame(FailingBrokerFactory.FAILURE, sve.getFailures()[0]); + } + + @Test + public void testValidateNeverReportsANullMessage() { + SchemaManager schemaManager = new SchemaManagerImpl(new SilentlyFailingBrokerFactory()); + + SchemaValidationException sve = + assertThrows(SchemaValidationException.class, schemaManager::validate); + assertEquals("Schema could not be validated: java.lang.IllegalStateException", sve.getMessage()); + } +}