From 5283bcd77d606369c021281bc904a2c257c4dd2a Mon Sep 17 00:00:00 2001 From: Jeremy Bauer Date: Tue, 7 Jul 2009 02:35:08 +0000 Subject: [PATCH] OPENJPA-1082 Added support for configurable bean validation groups git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@791678 13f79535-47bb-0310-9956-ffa450edef68 --- .../integration/validation/DefGrpEntity.java | 52 ++ .../validation/MixedGrpEntity.java | 76 +++ .../validation/NonDefGrpEntity.java | 65 ++ .../validation/TestValidationGroups.java | 639 ++++++++++++++++++ .../integration/validation/ValGroup1.java | 22 + .../integration/validation/ValGroup2.java | 22 + .../integration/validation/persistence.xml | 45 ++ .../openjpa/conf/OpenJPAConfiguration.java | 42 ++ .../conf/OpenJPAConfigurationImpl.java | 55 +- .../validation/ValidationUtils.java | 4 +- .../persistence/validation/ValidatorImpl.java | 109 ++- .../validation/localizer.properties | 3 +- 12 files changed, 1105 insertions(+), 29 deletions(-) create mode 100644 openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/DefGrpEntity.java create mode 100644 openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/MixedGrpEntity.java create mode 100644 openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/NonDefGrpEntity.java create mode 100644 openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/TestValidationGroups.java create mode 100644 openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup1.java create mode 100644 openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup2.java diff --git a/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/DefGrpEntity.java b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/DefGrpEntity.java new file mode 100644 index 000000000..f9dfd9053 --- /dev/null +++ b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/DefGrpEntity.java @@ -0,0 +1,52 @@ +/* + * 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.integration.validation; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; + +@Entity +public class DefGrpEntity { + + @Id + @GeneratedValue + private int id; + + // NotNull constraint with default validation group + @NotNull + private String dgName; + + public void setId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void setDgName(String dgName) { + this.dgName = dgName; + } + + public String getDgName() { + return dgName; + } +} diff --git a/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/MixedGrpEntity.java b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/MixedGrpEntity.java new file mode 100644 index 000000000..4b8aafa2c --- /dev/null +++ b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/MixedGrpEntity.java @@ -0,0 +1,76 @@ +/* + * 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.integration.validation; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; + +@Entity +public class MixedGrpEntity { + + @Id + @GeneratedValue + private int id; + + @NotNull + private String defNotNull; + + @NotNull(groups=ValGroup1.class) + private String vg1NotNull; + + @NotNull(groups=ValGroup2.class) + private String vg2NotNull; + + @NotNull(groups={ValGroup1.class, ValGroup2.class}) + private String vg12NotNull; + + public void setDefNotNull(String defNotNull) { + this.defNotNull = defNotNull; + } + + public String getDefNotNull() { + return defNotNull; + } + + public void setVg1NotNull(String vg1NotNull) { + this.vg1NotNull = vg1NotNull; + } + + public String getVg1NotNull() { + return vg1NotNull; + } + + public void setVg2NotNull(String vg2NotNull) { + this.vg2NotNull = vg2NotNull; + } + + public String getVg2NotNull() { + return vg2NotNull; + } + + public void setVg12NotNull(String vg12NotNull) { + this.vg12NotNull = vg12NotNull; + } + + public String getVg12NotNull() { + return vg12NotNull; + } +} diff --git a/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/NonDefGrpEntity.java b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/NonDefGrpEntity.java new file mode 100644 index 000000000..14b4803c1 --- /dev/null +++ b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/NonDefGrpEntity.java @@ -0,0 +1,65 @@ +/* + * 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.integration.validation; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; +import org.apache.openjpa.integration.validation.ValGroup1; + +@Entity +public class NonDefGrpEntity { + + @Id + @GeneratedValue + private int id; + + // NotNull constraint with default validation group + @NotNull + private String dgName; + + // NotNull constraint with specified validation group + @NotNull(groups=ValGroup1.class) + private String ndgName; + + public void setId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void setDgName(String dgName) { + this.dgName = dgName; + } + + public String getDgName() { + return dgName; + } + + public void setNdgName(String dgName) { + this.ndgName = dgName; + } + + public String getNdgName() { + return ndgName; + } +} diff --git a/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/TestValidationGroups.java b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/TestValidationGroups.java new file mode 100644 index 000000000..23e23a766 --- /dev/null +++ b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/TestValidationGroups.java @@ -0,0 +1,639 @@ +/* + * 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.integration.validation; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; + +import org.apache.openjpa.lib.log.Log; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; +import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.test.PersistenceTestCase; + +/** + * Tests the Bean Validation groups support as defined in the JPA 2.0 spec + * via the following scenarios: + * + * Verify default validation group on lifecycle events: + * 1a) PrePersist and PreUpdate validate with default validation group + * 1b) PreRemove does not validate with default validation group + * 1c) Specify the default group for PreRemove and verify that it validates with + * the default group. + * 1d) Verify validation for constraints using non-default validation groups + * does not occur. + * + * Verify validation occurs when specific validation groups are specified: + * 2a) Specify a non-default group for all lifecycle events. + * 2b) Specify multiple/mixed non-default groups for lifecycle events. + * + * Verify validation does not occur when no validation groups are specified: + * 3a) Specify an empty validation group for PrePersist and PreUpdate and + * verify validation does not occur on these events. + * + * @version $Rev$ $Date$ + */ +public class TestValidationGroups extends PersistenceTestCase { + + /** + * 1a) verify validation occurs using the default validation groups + * on pre-persist and pre-update on commit + */ + public void testDefaultValidationGroup() { + verifyDefaultValidationGroup(false); + } + + /** + * 1af) verify validation occurs using the default validation groups + * on pre-persist and pre-update on flush + */ + public void testDefaultValidationGroupFlush() { + verifyDefaultValidationGroup(true); + } + + + /** + * 1b) verify validation does not occur using the default validation group + * on the PreRemove lifecycle event on commit. + */ + public void testDefaultPreRemove() { + verifyDefaultPreRemove(false); + } + + /** + * 1bf) verify validation does not occur using the default validation group + * on the PreRemove lifecycle event on flush. + */ + public void testDefaultPreRemoveFlush() { + verifyDefaultPreRemove(true); + } + + /** + * 1c) verify validation occurs on the default group when default is + * specified for pre-remove on commit + */ + public void testSpecifiedDefaultPreRemove() { + verifySpecifiedDefaultPreRemove(true); + } + + /** + * 1cf) verify validation occurs on the default group when default is + * specified for pre-remove on flush + */ + public void testSpecifiedDefaultPreRemoveFlush() { + verifySpecifiedDefaultPreRemove(false); + } + + /** + * 2a) verify non-default validation group for all lifecycle events on + * commit. default validation group constraints should not validate + */ + public void testNonDefaultValidationGroup() { + verifyNonDefaultValidationGroup(false); + } + + /** + * 2af) verify non-default validation group for all lifecycle events on + * flush. default validation group constraints should not validate + */ + public void testNonDefaultValidationGroupFlush() { + verifyNonDefaultValidationGroup(true); + } + + /** + * 2b) verify multiple/mixed validation groups + * @param flush + */ + public void testMultipleValidationGroups() { + + // Configure persistence properties via map + Map propMap = new HashMap(); + propMap.put("javax.persistence.validation.group.pre-persist", + "org.apache.openjpa.integration.validation.ValGroup1," + + "org.apache.openjpa.integration.validation.ValGroup2"); + + propMap.put("javax.persistence.validation.group.pre-update", + ""); + + propMap.put("javax.persistence.validation.group.pre-remove", + "org.apache.openjpa.integration.validation.ValGroup2"); + + OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI) + OpenJPAPersistence.createEntityManagerFactory( + "multi-validation-group", + "org/apache/openjpa/integration/validation/persistence.xml", + propMap); + assertNotNull(emf); + // create EM + OpenJPAEntityManager em = emf.createEntityManager(); + assertNotNull(em); + + try { + MixedGrpEntity mge = new MixedGrpEntity(); + + // Assert vg1 and vg2 fire on pre-persist + try + { + em.getTransaction().begin(); + em.persist(mge); + em.getTransaction().commit(); + } catch (ConstraintViolationException e) { + checkCVE(e, + "vg1NotNull", + "vg2NotNull", + "vg12NotNull"); + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + // Assert no validation occurs on pre-update + // Persist an entity. Default should not validate on pre-persist + em.getTransaction().begin(); + mge.setVg1NotNull("Vg1"); + mge.setVg2NotNull("Vg2"); + mge.setVg12NotNull("Vg1&2"); + em.persist(mge); + em.getTransaction().commit(); + + + try { + em.getTransaction().begin(); + mge.setDefNotNull(null); + mge.setVg12NotNull(null); + mge.setVg1NotNull(null); + mge.setVg2NotNull(null); + em.getTransaction().commit(); + } catch (ConstraintViolationException e) { + fail("Update should have been successful." + + " Caught unexpected ConstraintViolationException."); + } + catch (Exception e) { + fail("Update should have been successful." + + " Caught unexpected exception."); + } + + // Update the entity again so that it can be cleaned up by the + // emf cleanup facility. The update should not validate + em.getTransaction().begin(); + mge.setVg2NotNull("Vg2NotNull"); + mge.setVg12NotNull("Vg12NotNull"); + em.getTransaction().commit(); + + // Assert vg2 and default groups validate on pre-remove + try { + em.getTransaction().begin(); + mge.setDefNotNull(null); + mge.setVg1NotNull(null); + mge.setVg2NotNull(null); + mge.setVg12NotNull(null); + em.remove(mge); + em.getTransaction().commit(); + } catch (ConstraintViolationException e) { + checkCVE(e, + "vg2NotNull", + "vg12NotNull"); + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + + } + finally { + cleanup(emf); + } + } + + /** + * 3a) No validation groups for pre-persist and pre-update and none for + * pre-remove by default. No validation should occur. + */ + public void testNoValidationGroups() { + + // Configure persistence properties via map + Map propMap = new HashMap(); + propMap.put("javax.persistence.validation.group.pre-persist",""); + + propMap.put("javax.persistence.validation.group.pre-update",""); + + OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI) + OpenJPAPersistence.createEntityManagerFactory( + "multi-validation-group", + "org/apache/openjpa/integration/validation/persistence.xml", + propMap); + assertNotNull(emf); + // create EM + OpenJPAEntityManager em = emf.createEntityManager(); + assertNotNull(em); + + try { + MixedGrpEntity mge = new MixedGrpEntity(); + + try + { + // No validation on pre-persist + em.getTransaction().begin(); + em.persist(mge); + em.getTransaction().commit(); + + // No validation on pre-update + em.getTransaction().begin(); + mge.setVg12NotNull(null); + em.getTransaction().commit(); + + // No validation on pre-remove + em.getTransaction().begin(); + em.remove(mge); + em.getTransaction().commit(); + } catch (ConstraintViolationException e) { + fail("Operations should have been successful." + + " Caught unexpected ConstraintViolationException."); + } + catch (Exception e) { + fail("Operations should have been successful." + + " Caught unexpected exception."); + } + } + finally { + cleanup(emf); + } + } + + private void verifyDefaultValidationGroup(boolean flush) { + OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI) + OpenJPAPersistence.createEntityManagerFactory( + "default-validation-group", + "org/apache/openjpa/integration/validation/persistence.xml"); + assertNotNull(emf); + // create EM + OpenJPAEntityManager em = emf.createEntityManager(); + assertNotNull(em); + try { + DefGrpEntity dge = new DefGrpEntity(); + // Test pre-persist with default group with flush after persist + // 1a) pre-persist + try { + em.getTransaction().begin(); + em.persist(dge); + if (flush) + em.flush(); + else + em.getTransaction().commit(); + fail("A ConstraintViolationException should have been thrown " + + "on pre-persist"); + } catch (ConstraintViolationException e) { + checkCVE(e, "dgName"); + // If flushing, tx should be marked for rollback + if (flush) { + assertTrue(em.getTransaction().isActive()); + assertTrue(em.getTransaction().getRollbackOnly()); + } + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + + // 1a) test pre-update with default group + // Add an entity with valid data (validation passes) + dge.setDgName("NonNullName"); + em.getTransaction().begin(); + em.persist(dge); + em.getTransaction().commit(); + try { + // Update the entity with null value. pre-update + // validation should fail on flush or commit + em.getTransaction().begin(); + dge.setDgName(null); + if (flush) + em.flush(); + else + em.getTransaction().commit(); + fail("A ConstraintViolationException should have been thrown " + + "on pre-update"); + } catch (ConstraintViolationException e) { + checkCVE(e, "dgName"); + // If flushing, tx should be marked for rollback + if (flush) { + assertTrue(em.getTransaction().isActive()); + assertTrue(em.getTransaction().getRollbackOnly()); + } + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + } + finally { + cleanup(emf); + } + } + + private void verifyNonDefaultValidationGroup(boolean flush) { + OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI) + OpenJPAPersistence.createEntityManagerFactory( + "non-default-validation-group", + "org/apache/openjpa/integration/validation/persistence.xml"); + assertNotNull(emf); + // create EM + OpenJPAEntityManager em = emf.createEntityManager(); + assertNotNull(em); + try { + NonDefGrpEntity ndge = new NonDefGrpEntity(); + // Test pre-persist with non-default group with flush after persist + try { + em.getTransaction().begin(); + em.persist(ndge); + if (flush) + em.flush(); + else + em.getTransaction().commit(); + fail("A ConstraintViolationException should have been thrown " + + "on pre-persist"); + } catch (ConstraintViolationException e) { + checkCVE(e, "ndgName"); + getLog(emf).trace("Caught expected exception"); + // If flushing, tx should be marked for rollback + if (flush) { + assertTrue(em.getTransaction().isActive()); + assertTrue(em.getTransaction().getRollbackOnly()); + } + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + + // pre-update with non-default group. default group + // validation should not occur + // Add an entity with valid data (validation passes) + try { + ndge.setNdgName("NonNullName"); + ndge.setDgName(null); + em.getTransaction().begin(); + em.persist(ndge); + em.getTransaction().commit(); + getLog(emf).trace("Entity was persisted. As expected, no " + + "validation took place on pre-persist with default group."); + } + catch (ConstraintViolationException e) { + fail("Caught unexpected exception"); + if (em.getTransaction().isActive()) + em.getTransaction().rollback(); + } + try { + // Update the entity with null value. pre-update + // validation should fail on flush or commit + + em.getTransaction().begin(); + ndge.setNdgName(null); + if (flush) + em.flush(); + else + em.getTransaction().commit(); + fail("A ConstraintViolationException should have been thrown " + + "on pre-update"); + } catch (ConstraintViolationException e) { + checkCVE(e, "ndgName"); + // If flushing, tx should be marked for rollback + if (flush) { + assertTrue(em.getTransaction().isActive()); + assertTrue(em.getTransaction().getRollbackOnly()); + } + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + + // Merge the entity so that it can be removed. + em.getTransaction().begin(); + ndge.setDgName(null); + ndge.setNdgName("Some name"); + ndge = em.merge(ndge); + em.getTransaction().commit(); + + try { + // Update the entity with null value and remove the entity. + // validation should not fail on pre-remove + em.getTransaction().begin(); + ndge.setNdgName(null); + em.remove(ndge); + if (flush) + em.flush(); + else + em.getTransaction().commit(); + fail("A ConstraintViolationException should have been thrown " + + "on pre-remove"); + } catch (ConstraintViolationException e) { + checkCVE(e, "ndgName"); + // If flushing, tx should be marked for rollback + if (flush) { + assertTrue(em.getTransaction().isActive()); + assertTrue(em.getTransaction().getRollbackOnly()); + } + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + + } + finally { + cleanup(emf); + } + } + + /** + * verify validation does not occur using the default validation group + * on the PreRemove lifecycle event. + */ + public void verifyDefaultPreRemove(boolean flush) { + OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI) + OpenJPAPersistence.createEntityManagerFactory( + "default-validation-group", + "org/apache/openjpa/integration/validation/persistence.xml"); + assertNotNull(emf); + // create EM + OpenJPAEntityManager em = emf.createEntityManager(); + assertNotNull(em); + + try { + // Add an entity + DefGrpEntity dge = new DefGrpEntity(); + dge.setDgName("NonNullName"); + em.getTransaction().begin(); + em.persist(dge); + em.getTransaction().commit(); + try { + // Update the entity with null value and remove the entity. + // validation should not fail on pre-remove + em.getTransaction().begin(); + dge.setDgName(null); + em.remove(dge); + if (flush) + em.flush(); + else + em.getTransaction().commit(); + getLog(emf).trace("Entity was removed. As expected, no " + + "validation took place on pre-remove."); + } catch (ConstraintViolationException e) { + fail("Should not have caught a ConstraintViolationException"); + getLog(emf).trace("Caught expected exception"); + } + catch (Exception e) { + fail("Should not have caught an Exception"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + } + finally { + cleanup(emf); + } + } + + /** + * verify validation occurs when the default validation group + * is specified for the PreRemove lifecycle event via the + * "javax.persistence.validation.group.pre-remove" property. + */ + public void verifySpecifiedDefaultPreRemove(boolean flush) { + OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI) + OpenJPAPersistence.createEntityManagerFactory( + "pre-remove-default-validation-group", + "org/apache/openjpa/integration/validation/persistence.xml"); + assertNotNull(emf); + // create EM + OpenJPAEntityManager em = emf.createEntityManager(); + assertNotNull(em); + + try { + // Add an entity + DefGrpEntity dge = new DefGrpEntity(); + dge.setDgName("NonNullName"); + em.getTransaction().begin(); + em.persist(dge); + em.getTransaction().commit(); + try { + // Update the entity with null value and remove the entity. + // validation should not fail on pre-remove + em.getTransaction().begin(); + dge.setDgName(null); + em.remove(dge); + if (flush) + em.flush(); + else + em.getTransaction().commit(); + fail("A ConstraintViolationException should have been thrown " + + "on pre-remove"); + } catch (ConstraintViolationException e) { + checkCVE(e, "dgName"); + // If flushing, tx should be marked for rollback + if (flush) { + assertTrue(em.getTransaction().isActive()); + assertTrue(em.getTransaction().getRollbackOnly()); + } + } + catch (Exception e) { + fail("Should have caught a ConstraintViolationException"); + } + finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + } + finally { + cleanup(emf); + } + } + + private void checkCVE(ConstraintViolationException e, + String... vioProperties) { + Setcvs = e.getConstraintViolations(); + if (vioProperties.length == 0 && cvs == null) + return; + assertEquals(vioProperties.length, cvs.size()); + Iterator> i = + (Iterator>) cvs.iterator(); + while (i.hasNext()) { + ConstraintViolation v = (ConstraintViolation)i.next(); + boolean found = false; + for (String vio : vioProperties) { + if (v.getPropertyPath().equals(vio)) { + found = true; + break; + } + } + if (!found) + fail("Unexpected ConstraintViolation for: " + + v.getPropertyPath()); + } + } + + /** + * Remove entities and close the emf an any open em's. + * @param emf + */ + private void cleanup(OpenJPAEntityManagerFactorySPI emf) { + clear(emf); + closeEMF(emf); + } + + /** + * Internal convenience method for getting the OpenJPA logger + * + * @return + */ + private Log getLog(OpenJPAEntityManagerFactorySPI emf) { + return emf.getConfiguration().getLog("Tests"); + } +} diff --git a/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup1.java b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup1.java new file mode 100644 index 000000000..052253c79 --- /dev/null +++ b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup1.java @@ -0,0 +1,22 @@ +/* + * 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.integration.validation; + +public interface ValGroup1 { +} diff --git a/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup2.java b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup2.java new file mode 100644 index 000000000..77a1a5fb1 --- /dev/null +++ b/openjpa-integration/validation/src/test/java/org/apache/openjpa/integration/validation/ValGroup2.java @@ -0,0 +1,22 @@ +/* + * 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.integration.validation; + +public interface ValGroup2 { +} diff --git a/openjpa-integration/validation/src/test/resources/org/apache/openjpa/integration/validation/persistence.xml b/openjpa-integration/validation/src/test/resources/org/apache/openjpa/integration/validation/persistence.xml index dc82014b9..7b2703d75 100644 --- a/openjpa-integration/validation/src/test/resources/org/apache/openjpa/integration/validation/persistence.xml +++ b/openjpa-integration/validation/src/test/resources/org/apache/openjpa/integration/validation/persistence.xml @@ -69,5 +69,50 @@ org.apache.openjpa.integration.validation.ConstraintNull CALLBACK + + + org.apache.openjpa.integration.validation.DefGrpEntity + CALLBACK + + + + + + + org.apache.openjpa.integration.validation.DefGrpEntity + CALLBACK + + + + + + + + org.apache.openjpa.integration.validation.NonDefGrpEntity + CALLBACK + + + + + + + + + + org.apache.openjpa.integration.validation.MixedGrpEntity + CALLBACK + + + + + diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java index 7bdab4792..cee1a338a 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java @@ -1773,4 +1773,46 @@ public interface OpenJPAConfiguration * @since 2.0.0 */ public void setWriteBehindCallback(String wbcallback); + + /** + * Gets the validation groups for pre-persist + * + * @Since 2.0.0 + */ + public String getValidationGroupPrePersist(); + + /** + * Sets the validation groups for pre-persist + * + * @Since 2.0.0 + */ + public void setValidationGroupPrePersist(String vgPrePersist); + + /** + * Gets the validation groups for pre-update + * + * @Since 2.0.0 + */ + public String getValidationGroupPreUpdate(); + + /** + * Sets the validation groups for pre-update + * + * @Since 2.0.0 + */ + public void setValidationGroupPreUpdate(String vgPreUpdate); + + /** + * Gets the validation groups for pre-remove + * + * @Since 2.0.0 + */ + public String getValidationGroupPreRemove(); + + /** + * Sets the validation groups for pre-remove + * + * @Since 2.0.0 + */ + public void setValidationGroupPreRemove(String vgPreRemove); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java index eec2df207..326c46c2f 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java @@ -164,7 +164,9 @@ public class OpenJPAConfigurationImpl public ObjectValue validationFactory; public ObjectValue validator; public ObjectValue lifecycleEventManager; - + public StringValue validationGroupPrePersist; + public StringValue validationGroupPreUpdate; + public StringValue validationGroupPreRemove; public ObjectValue writeBehindCachePlugin; public ObjectValue writeBehindCacheManagerPlugin; public ObjectValue writeBehindCallbackPlugin; @@ -574,6 +576,24 @@ public class OpenJPAConfigurationImpl validationMode = addString("javax.persistence.validation.mode"); validationMode.setDynamic(true); + String defValidationGroup = "javax.validation.groups.Default"; + validationGroupPrePersist = + addString("javax.persistence.validation.group.pre-persist"); + validationGroupPrePersist.setString(defValidationGroup); + validationGroupPrePersist.setDefault(""); + validationGroupPrePersist.setDynamic(true); + + validationGroupPreUpdate = + addString("javax.persistence.validation.group.pre-update"); + validationGroupPreUpdate.setString(defValidationGroup); + validationGroupPreUpdate.setDefault(""); + validationGroupPreUpdate.setDynamic(true); + + validationGroupPreRemove = + addString("javax.persistence.validation.group.pre-remove"); + validationGroupPreRemove.setDefault(""); + validationGroupPreRemove.setDynamic(true); + validationFactory = addObject("javax.persistence.validation.factory"); validationFactory.setInstantiatingGetter( "getValidationFactoryInstance"); @@ -1592,6 +1612,39 @@ public class OpenJPAConfigurationImpl return mode; } + public void setValidationGroupPrePersist(String vgPrePersist) { + validationGroupPrePersist.setString(vgPrePersist); + } + + public String getValidationGroupPrePersist() { + String vgPrePersist = validationGroupPrePersist.getString(); + if (vgPrePersist == null) + vgPrePersist = validationGroupPrePersist.getDefault(); + return vgPrePersist; + } + + public void setValidationGroupPreUpdate(String vgPreUpdate) { + validationGroupPreUpdate.setString(vgPreUpdate); + } + + public String getValidationGroupPreUpdate() { + String vgPreUpdate = validationGroupPreUpdate.getString(); + if (vgPreUpdate == null) + vgPreUpdate = validationGroupPreUpdate.getDefault(); + return vgPreUpdate; + } + + public void setValidationGroupPreRemove(String vgPreRemove) { + validationGroupPreRemove.setString(vgPreRemove); + } + + public String getValidationGroupPreRemove() { + String vgPreRemove = validationGroupPreRemove.getString(); + if (vgPreRemove == null) + vgPreRemove = validationGroupPreRemove.getDefault(); + return vgPreRemove; + } + public void instantiateAll() { super.instantiateAll(); getMetaDataRepositoryInstance(); diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidationUtils.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidationUtils.java index 1282390a0..eef025736 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidationUtils.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidationUtils.java @@ -97,9 +97,7 @@ public class ValidationUtils { // we have the javax.validation APIs try { // try loading a validation provider - ValidatorImpl validator = new ValidatorImpl( - conf.getValidationFactoryInstance(), - conf.getValidationMode()); + ValidatorImpl validator = new ValidatorImpl(conf); // set the Validator into the config conf.setValidatorInstance(validator); // update the LifecycleEventManager plugin to use it diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidatorImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidatorImpl.java index b5419e485..f6c703075 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidatorImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/validation/ValidatorImpl.java @@ -30,7 +30,9 @@ import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; +import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.event.LifecycleEvent; +import org.apache.openjpa.lib.conf.Configuration; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.validation.AbstractValidator; import org.apache.openjpa.validation.ValidationException; @@ -43,6 +45,8 @@ public class ValidatorImpl extends AbstractValidator { private ValidatorFactory _validatorFactory = null; private Validator _validator = null; private ValidationMode _mode = ValidationMode.AUTO; + private OpenJPAConfiguration _conf = null; + // A map storing the validation groups to use for a particular event type private Map[]> _validationGroups = @@ -61,7 +65,7 @@ public class ValidatorImpl extends AbstractValidator { static { _vgMapping.put(VG_PRE_PERSIST, - LifecycleEvent.BEFORE_STORE); + LifecycleEvent.BEFORE_PERSIST); _vgMapping.put(VG_PRE_REMOVE, LifecycleEvent.BEFORE_DELETE); _vgMapping.put(VG_PRE_UPDATE, @@ -77,6 +81,25 @@ public class ValidatorImpl extends AbstractValidator { initialize(); } + public ValidatorImpl(Configuration conf) { + if (conf instanceof OpenJPAConfiguration) { + _conf = (OpenJPAConfiguration)conf; + Object validatorFactory = _conf.getValidationFactoryInstance(); + String mode = _conf.getValidationMode(); + _mode = Enum.valueOf(ValidationMode.class, mode); + if (validatorFactory != null) { + if (validatorFactory instanceof ValidatorFactory) { + _validatorFactory = (ValidatorFactory)validatorFactory; + } else { + // Supplied object was not an instance of a ValidatorFactory + throw new IllegalArgumentException( + _loc.get("invalid-factory").getMessage()); + } + } + } + initialize(); + } + /** * Type-specific constructor * Returns an Exception if a Validator could not be created. @@ -95,27 +118,6 @@ public class ValidatorImpl extends AbstractValidator { initialize(); } - /** - * Generic-type constructor - * Returns an Exception if a Validator could not be created. - * @param validatorFactory an instance to the validatorFactory - * @param mode validation mode enum as string value - */ - public ValidatorImpl(Object validatorFactory, - String mode) { - _mode = Enum.valueOf(ValidationMode.class, mode); - if (validatorFactory != null) { - if (validatorFactory instanceof ValidatorFactory) { - _validatorFactory = (ValidatorFactory)validatorFactory; - } else { - // Supplied object was not an instance of a ValidatorFactory. - throw new IllegalArgumentException( - _loc.get("invalid-factory").getMessage()); - } - } - initialize(); - } - /** * Common setup code factored out of the constructors */ @@ -140,8 +142,19 @@ public class ValidatorImpl extends AbstractValidator { _loc.get("no-validator").getMessage()); } - // add in default validation groups, which can be over-ridden later - addDefaultValidationGroups(); + if (_conf != null) { + addValidationGroup(VG_PRE_PERSIST, + _conf.getValidationGroupPrePersist()); + addValidationGroup(VG_PRE_UPDATE, + _conf.getValidationGroupPreUpdate()); + addValidationGroup(VG_PRE_REMOVE, + _conf.getValidationGroupPreRemove()); + + } + else { + // add in default validation groups, which can be over-ridden later + addDefaultValidationGroups(); + } } else { // A Validator should not be created based on the supplied ValidationMode. throw new RuntimeException( @@ -181,6 +194,54 @@ public class ValidatorImpl extends AbstractValidator { _validationGroups.put(event, validationGroup); } + /** + * Add the validation group(s) for the specified event. Event definitions + * are defined in LifecycleEvent + * @param event + * @param group + */ + public void addValidationGroup(String validationGroupName, String group) { + Integer event = findEvent(validationGroupName); + if (event != null) { + Class[] vgs = getValidationGroup(validationGroupName, group); + if (vgs != null) { + addValidationGroup(event, vgs); + } + } + else { + // There were no events found for group "{0}". + throw new IllegalArgumentException( + _loc.get("no-group-events", validationGroupName).getMessage()); + } + } + + /** + * Converts a comma separated list of validation groups into an array + * of classes. + * @param group + * @return + */ + private Class[] getValidationGroup(String vgName, String group) { + Class[] vgGrp = null; + if (group == null || group.trim().length() == 0) { + return null; + } + String[] strClasses = group.split(","); + if (strClasses.length > 0) { + vgGrp = new Class[strClasses.length]; + for (int i = 0; i < strClasses.length; i++) { + try { + vgGrp[i] = Class.forName(strClasses[i]); + } catch (Throwable t) { + throw new IllegalArgumentException( + _loc.get("invalid-validation-group", strClasses[i], + vgName).getMessage(), t); + } + } + } + return vgGrp; + } + /** * Return the validation groups to be validated for a specified event * @param event Lifecycle event id diff --git a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/validation/localizer.properties b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/validation/localizer.properties index 6e905f5f6..8e594b279 100644 --- a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/validation/localizer.properties +++ b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/validation/localizer.properties @@ -34,4 +34,5 @@ validate-property-failed: A validation constraint failure occurred for \ property "{1}" in class "{0}". validate-value-failed: A validation constraint failure occurred for \ value "{2}" of property "{1}" in class "{0}". - +invalid-validation-group: The class "{0}" specified in the validation group \ + property "{1}" is not valid or could not be loaded.