diff --git a/openjpa-examples/image-gallery/pom.xml b/openjpa-examples/image-gallery/pom.xml new file mode 100644 index 000000000..42f91583a --- /dev/null +++ b/openjpa-examples/image-gallery/pom.xml @@ -0,0 +1,169 @@ + + + + + 4.0.0 + + + org.apache.openjpa + openjpa-parent + 2.1.0-SNAPSHOT + + + org.apache.openjpa.openjpa-examples + image-gallery + jar + + Apache OpenJPA :: Examples - image-gallery + http://openjpa.apache.org/samples.html + + + UTF-8 + DefaultLevel=WARN + ../../openjpa-project/checkstyle.xml + 256m + 1024m + -Xmx${test.jvm.maxheapsize} -XX:MaxPermSize=${test.jvm.maxpermsize} + ${test.jvm.arguments} + + + + + + + + commons-lang + commons-lang + 2.4 + test + + + commons-beanutils + commons-beanutils + 1.8.3 + test + + + org.apache.geronimo.specs + geronimo-validation_1.0_spec + 1.0 + + + org.apache.bval + org.apache.bval.bundle + 0.1-incubating-SNAPSHOT + test + + + org.apache.openjpa + openjpa-all + ${pom.version} + + + org.apache.derby + derby + 10.5.3.0_1 + test + + + junit + junit + 3.8.1 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.1 + + 1.6 + 1.6 + + -proc:none + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.3 + + + + true + true + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.4.3 + + ${surefire.jvm.args} + false + false + true + + + + + + + + org.codehaus.mojo + openjpa-maven-plugin + 1.1 + + org/apache/openjpa/examples/gallery/model/*.class + true + true + src/test/resources/META-INF/persistence.xml + + + + enhancer + process-classes + + enhance + + + + + + org.apache.openjpa + openjpa-all + ${pom.version} + + + + + + diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/ImageType.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/ImageType.java new file mode 100644 index 000000000..4dd40d249 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/ImageType.java @@ -0,0 +1,24 @@ +/* + * 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.example.gallery; + +public enum ImageType { + JPEG, + GIF +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageConstraint.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageConstraint.java new file mode 100644 index 000000000..0d7b4dc55 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageConstraint.java @@ -0,0 +1,45 @@ +/* + * 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.example.gallery.constraint; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import org.apache.openjpa.example.gallery.ImageType; + +/** + * Type-level annotation used to specify an image constraint. Uses + * ImageValidator to perform the validation. + */ +@Documented +@Constraint(validatedBy = ImageValidator.class) +@Target({ TYPE }) +@Retention(RUNTIME) +public @interface ImageConstraint { + String message() default "Image data is not a supported format."; + Class[] groups() default {}; + Class[] payload() default {}; + ImageType[] value() default { ImageType.GIF, ImageType.JPEG }; +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContent.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContent.java new file mode 100644 index 000000000..024c1cad2 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContent.java @@ -0,0 +1,46 @@ +/* + * 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.example.gallery.constraint; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import org.apache.openjpa.example.gallery.ImageType; + +/** + * Attribute-level annotation used to specify an image content constraint. Uses + * ImageContentValidator to perform the validation. + */ +@Documented +@Constraint(validatedBy = ImageContentValidator.class) +@Target({ METHOD, FIELD }) +@Retention(RUNTIME) +public @interface ImageContent { + String message() default "Image data is not a supported format."; + Class[] groups() default {}; + Class[] payload() default {}; + ImageType[] value() default { ImageType.GIF, ImageType.JPEG }; +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContentValidator.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContentValidator.java new file mode 100644 index 000000000..6b0917e12 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContentValidator.java @@ -0,0 +1,72 @@ +/* + * 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.example.gallery.constraint; + +import java.util.Arrays; +import java.util.List; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.apache.openjpa.example.gallery.ImageType; + +/** + * Simple check that file format is of a supported type + */ +public class ImageContentValidator implements ConstraintValidator { + + private List allowedTypes = null; + /** + * Configure the constraint validator based on the image + * types it should support. + * @param constraint the constraint definition + */ + public void initialize(ImageContent constraint) { + allowedTypes = Arrays.asList(constraint.value()); + } + + /** + * Validate a specified value. + */ + public boolean isValid(byte[] value, ConstraintValidatorContext context) { + if (value == null) { + return false; + } + // Verify the GIF header is either GIF87 or GIF89 + if (allowedTypes.contains(ImageType.GIF)) { + String gifHeader = new String(value, 0, 6); + if (value.length >= 6 && + (gifHeader.equalsIgnoreCase("GIF87a") || + gifHeader.equalsIgnoreCase("GIF89a"))) { + return true; + } + } + // Verify the JPEG begins with SOI & ends with EOI + if (allowedTypes.contains(ImageType.JPEG)) { + if (value.length >= 4 && + value[0] == 0xff && value[1] == 0xd8 && + value[value.length - 2] == 0xff && + value[value.length -1] == 0xd9) { + return true; + } + } + // Unknown file format + return false; + } +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageGroup.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageGroup.java new file mode 100644 index 000000000..6cf7ada16 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageGroup.java @@ -0,0 +1,26 @@ +/* + * 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.example.gallery.constraint; + +/** + * Simple interface used to specify validation group: ImageGroup + */ +public interface ImageGroup { + +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageValidator.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageValidator.java new file mode 100644 index 000000000..105e18250 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageValidator.java @@ -0,0 +1,90 @@ +/* + * 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.example.gallery.constraint; + +import java.util.Arrays; +import java.util.List; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.apache.openjpa.example.gallery.ImageType; +import org.apache.openjpa.example.gallery.model.Image; + +/** + * Simple validator used to verify that image data is of a supported type + */ +public class ImageValidator implements ConstraintValidator { + + private List allowedTypes = null; + /** + * Configure the constraint validator based on the image + * types it should support. + * @param constraint the constraint definition + */ + public void initialize(ImageConstraint constraint) { + allowedTypes = Arrays.asList(constraint.value()); + } + + /** + * Validate a specified value. + */ + public boolean isValid(Image value, ConstraintValidatorContext context) { + // JSR-303 best practice. Promotes the use of @NotNull to perform + // null checking. + if (value == null) { + return true; + } + + // All these values will be pre-validated with @NotNull constraints + // so they are safe to use + byte[] data = value.getData(); + String fileName = value.getFileName(); + ImageType type = value.getType(); + + // Verify the GIF type is correct5, has the correct extension and + // the data header is either GIF87 or GIF89 + if (allowedTypes.contains(ImageType.GIF) && + type == ImageType.GIF && + fileName.endsWith(".gif")) { + if (data != null && data.length >= 6) { + String gifHeader = new String(data, 0, 6); + if (gifHeader.equalsIgnoreCase("GIF87a") || + gifHeader.equalsIgnoreCase("GIF89a")) { + return true; + } + } + } + // Verify the JPEG type is correct, has the correct extension and + // the data begins with SOI & ends with EOI markers + if (allowedTypes.contains(ImageType.JPEG) && + value.getType() == ImageType.JPEG && + (fileName.endsWith(".jpg") || + fileName.endsWith(".jpeg"))) { + if (data.length >= 4 && + data[0] == 0xff && data[1] == 0xd8 && + data[data.length - 2] == 0xff && + data[data.length - 1] == 0xd9) { + return true; + } + } + // Unknown file format + return false; + } +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/SequencedImageGroup.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/SequencedImageGroup.java new file mode 100644 index 000000000..421e63cfa --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/SequencedImageGroup.java @@ -0,0 +1,32 @@ +/* + * 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.example.gallery.constraint; + +import javax.validation.GroupSequence; +import javax.validation.groups.Default; + +/** + * Sequenced validation group definition. When this validation group is + * used constraints within the Default group will validate first. If + * successful, constraints within the ImageGroup will validate next. + */ +@GroupSequence({Default.class, ImageGroup.class}) +public interface SequencedImageGroup { + +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Album.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Album.java new file mode 100644 index 000000000..731aa1df1 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Album.java @@ -0,0 +1,81 @@ +/* + * 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.example.gallery.model; + +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.ManyToMany; +import javax.validation.constraints.NotNull; + +/** + * Album entity. Contains references to zero or more images and zero or more + * image creators/authors. + */ +@Entity +public class Album { + + @Id + @GeneratedValue + private long id; + + @NotNull + private String name; + + @OneToMany + private List images; + + @ManyToMany + private List creator; + + public void setId(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setImages(List images) { + this.images = images; + } + + public List getImages() { + return images; + } + + public void setCreator(List creator) { + this.creator = creator; + } + + public List getCreator() { + return creator; + } +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Creator.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Creator.java new file mode 100644 index 000000000..419b61920 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Creator.java @@ -0,0 +1,94 @@ +/* + * 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.example.gallery.model; + +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; +import javax.validation.constraints.NotNull; + +/** + * Creator entity. Contains a reference to one or more images authored by + * an instance of the creator. + */ +@Entity +public class Creator { + + @Id + @GeneratedValue + private long id; + + @NotNull(message="Photographer's first name must be specified.") + private String firstName; + + @NotNull(message="Photographer's last name must be specified.") + private String lastName; + + @OneToMany + @OrderBy("fileName") + private List images; + + @ManyToMany + private List albums; + + public void setId(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getFirstName() { + return firstName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getLastName() { + return lastName; + } + + public void setImages(List images) { + this.images = images; + } + + public List getImages() { + return images; + } + + public List getAlbums() { + return albums; + } + + public void setAlbums(List albums) { + this.albums = albums; + } +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Image.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Image.java new file mode 100644 index 000000000..78fcaf298 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Image.java @@ -0,0 +1,105 @@ +/* + * 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.example.gallery.model; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +import org.apache.openjpa.example.gallery.ImageType; +import org.apache.openjpa.example.gallery.constraint.ImageConstraint; +import org.apache.openjpa.example.gallery.constraint.ImageGroup; + +/** + * Image entity which makes use of several BV constraints. + */ +@Entity +@ImageConstraint(groups=ImageGroup.class) +public class Image { + + private long id; + private ImageType type; + private String fileName; + private byte[] data; + private Location location; + private Creator creator; + + @Id + @GeneratedValue + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @NotNull(message="Image type must be specified.") + @Enumerated(EnumType.STRING) + public ImageType getType() { + return type; + } + + public void setType(ImageType type) { + this.type = type; + } + + @NotNull(message="Image file name must not be null.") + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + @NotNull(message="Image data must not be null.") + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + @Valid + @Embedded + public Location getLocation() { + return location; + } + + public void setLocation(Location location) { + this.location = location; + } + + @ManyToOne + public Creator getCreator() { + return creator; + } + + public void setCreator(Creator creator) { + this.creator = creator; + } +} diff --git a/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Location.java b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Location.java new file mode 100644 index 000000000..b8e144ff0 --- /dev/null +++ b/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Location.java @@ -0,0 +1,72 @@ +package org.apache.openjpa.example.gallery.model; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * Location embeddable with several BV constraints applied. + */ +@Embeddable +public class Location { + + @NotNull(message="City must be specified.") + private String city; + + private String street; + + private String state; + + @NotNull(message="Country must be specified.") + @Size(message="Country must be 50 characters or less.", max=50) + @Column(length=50) + private String country; + + @Size(message="Zip code must be 10 characters or less.", max=10) + @Pattern(message="Zip code must be 5 digits or use the 5+4 format.", + regexp="^\\d{5}(([\\-]|[\\+])\\d{4})?$") + @Column(length=10) + private String zipCode; + + public void setCity(String city) { + this.city = city; + } + + public String getCity() { + return city; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getStreet() { + return street; + } + + public void setState(String state) { + this.state = state; + } + + public String getState() { + return state; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getCountry() { + return country; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } + + public String getZipCode() { + return zipCode; + } +} diff --git a/openjpa-examples/image-gallery/src/test/java/org/apache/openjpa/example/gallery/TestJPAValidation.java b/openjpa-examples/image-gallery/src/test/java/org/apache/openjpa/example/gallery/TestJPAValidation.java new file mode 100644 index 000000000..5e80f5686 --- /dev/null +++ b/openjpa-examples/image-gallery/src/test/java/org/apache/openjpa/example/gallery/TestJPAValidation.java @@ -0,0 +1,122 @@ +package org.apache.openjpa.example.gallery; + +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; + +import org.apache.openjpa.example.gallery.model.Image; +import org.apache.openjpa.example.gallery.model.Location; + +public class TestJPAValidation extends junit.framework.TestCase { + + /** + * Shows usage of BV constraints with JPA at pre-update, pre-remove, + * and pre-persist lifecycle events. + */ + public void testValidation() { + EntityManagerFactory emf = + Persistence.createEntityManagerFactory("BeanValidation"); + EntityManager em = emf.createEntityManager(); + + // Create a valid location + Location loc = new Location(); + loc.setCity("Rochester"); + loc.setState("MN"); + loc.setZipCode("55901"); + loc.setCountry("USA"); + + // Create an Image with non-matching type and file extension + Image img = new Image(); + img.setType(ImageType.JPEG); + img.setFileName("Winter_01.gif"); + loadImage(img); + img.setLocation(loc); + + // *** PERSIST *** + try { + em.getTransaction().begin(); + System.out.println("Persisting an entity with non-matching extension and type"); + em.persist(img); + fail(); + } catch (ConstraintViolationException cve) { + // Transaction was marked for rollback, roll it back and + // start a new TX + em.getTransaction().rollback(); + handleConstraintViolation(cve); + em.getTransaction().begin(); + System.out.println("Fixing the file type and re-attempting the persist."); + img.setType(ImageType.GIF); + em.persist(img); + em.getTransaction().commit(); + System.out.println("Persist was successful"); + } + + // *** UPDATE *** + try { + em.getTransaction().begin(); + // Modify the file name to a non-matching file name + // and commit to trigger an update + System.out.println("Modifying file name to use an extension that does not"); + System.out.println("match the file type. This will cause a CVE."); + img.setFileName("Winter_01.jpg"); + em.getTransaction().commit(); + fail(); + } catch (ConstraintViolationException cve) { + // Handle the exception. The commit failed so the transaction + // was already rolled back. + System.out.println("Update failed as expected"); + handleConstraintViolation(cve); + } + // The update failure caused img to be detached. It must be merged back + // into the persistence context. + img = em.merge(img); + + // *** REMOVE *** + em.getTransaction().begin(); + try { + // Remove the type and commit to trigger removal + System.out.println("Setting the type to an invalid type. This will cause a"); + System.out.println("validation exception upon removal"); + img.setType(null); + em.remove(img); + em.getTransaction().commit(); + fail(); + } catch (ConstraintViolationException cve) { + // Rollback the active transaction and handle the exception + em.getTransaction().rollback(); + System.out.println("Remove failed as expected"); + handleConstraintViolation(cve); + } + em.close(); + emf.close(); + System.out.println("Done"); + } + + // Handles constraint violations by printing out violation information + private static void handleConstraintViolation(ConstraintViolationException cve) { + Set> cvs = cve.getConstraintViolations(); + for (ConstraintViolation cv : cvs) { + System.out.println("------------------------------------------------"); + System.out.println("Violation: " + cv.getMessage()); + System.out.println("Entity: " + cv.getRootBeanClass().getSimpleName()); + // The violation occurred on a leaf bean (embeddable) + if (cv.getLeafBean() != null && cv.getRootBean() != cv.getLeafBean()) { + System.out.println("Embeddable: " + cv.getLeafBean().getClass().getSimpleName()); + } + System.out.println("Attribute: " + cv.getPropertyPath()); + System.out.println("Invalid value: " + cv.getInvalidValue()); + System.out.println("------------------------------------------------"); + } + } + + // Mock image loading utility... simply loads the GIF89a header to satisfy the + // constraint validator. + private static void loadImage(Image img) { + img.setData(new byte[] { 'G', 'I', 'F', '8', '9', 'a' }); + } + +} diff --git a/openjpa-examples/image-gallery/src/test/resources/META-INF/persistence.xml b/openjpa-examples/image-gallery/src/test/resources/META-INF/persistence.xml new file mode 100644 index 000000000..c4914cd66 --- /dev/null +++ b/openjpa-examples/image-gallery/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,50 @@ + + + + + + Example persistence unit for Bean Validation + org.apache.openjpa.example.gallery.model.Album + org.apache.openjpa.example.gallery.model.Creator + org.apache.openjpa.example.gallery.model.Image + org.apache.openjpa.example.gallery.model.Location + true + CALLBACK + + + + + + + + + + + + \ No newline at end of file