diff --git a/deltaspike/.gitignore b/deltaspike/.gitignore new file mode 100644 index 0000000000..aa121e866e --- /dev/null +++ b/deltaspike/.gitignore @@ -0,0 +1,3 @@ +/.idea +baeldung-jee7-seed.iml +/target diff --git a/deltaspike/pom.xml b/deltaspike/pom.xml new file mode 100644 index 0000000000..22cf8f935c --- /dev/null +++ b/deltaspike/pom.xml @@ -0,0 +1,301 @@ + + + 4.0.0 + com.baeldung + deltaspike + 1.0 + war + deltaspike + A starter Java EE 7 webapp which uses DeltaSpike + + http://wildfly.org + + + Apache License, Version 2.0 + repo + http://www.apache.org/licenses/LICENSE-2.0.html + + + + + + + UTF-8 + + + 1.0.2.Final + + + 8.2.1.Final + + + 3.1 + 2.16 + 2.5 + + + 1.7 + 1.7 + + + + + + + + org.wildfly.bom + jboss-javaee-7.0-with-tools + ${version.jboss.bom} + pom + import + + + org.wildfly.bom + jboss-javaee-7.0-with-hibernate + ${version.jboss.bom} + pom + import + + + + + + + + + + + javax.enterprise + cdi-api + provided + + + + + org.jboss.spec.javax.annotation + jboss-annotations-api_1.2_spec + provided + + + + + org.jboss.resteasy + jaxrs-api + provided + + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + provided + + + + + org.jboss.spec.javax.ejb + jboss-ejb-api_3.2_spec + provided + + + + + + + org.hibernate + hibernate-validator + provided + + + org.slf4j + slf4j-api + + + + + + + org.jboss.spec.javax.faces + jboss-jsf-api_2.2_spec + provided + + + + + + + org.hibernate + hibernate-jpamodelgen + provided + + + + + org.hibernate + hibernate-validator-annotation-processor + provided + + + + + junit + junit + test + + + + + + org.jboss.arquillian.junit + arquillian-junit-container + test + + + + org.jboss.arquillian.protocol + arquillian-protocol-servlet + test + + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-impl-maven + test + + + + org.apache.deltaspike.modules + deltaspike-data-module-api + 1.7.1 + compile + + + + org.apache.deltaspike.modules + deltaspike-data-module-impl + 1.7.1 + runtime + + + + + com.mysema.querydsl + querydsl-apt + 3.7.4 + provided + + + + com.mysema.querydsl + querydsl-jpa + 3.7.4 + + + + org.slf4j + slf4j-log4j12 + 1.6.1 + + + + + + ${project.artifactId} + + + maven-war-plugin + ${version.war.plugin} + + + false + + + + com.mysema.maven + apt-maven-plugin + 1.0.9 + + + + process + + + target/generated-sources/java + com.mysema.query.apt.jpa.JPAAnnotationProcessor + + + + + + + + org.wildfly.plugins + wildfly-maven-plugin + ${version.wildfly.maven.plugin} + + + + + + + + + default + + true + + + + + maven-surefire-plugin + ${version.surefire.plugin} + + true + + + + + + + + + + + + arq-wildfly-managed + + + org.wildfly + wildfly-arquillian-container-managed + test + + + + + diff --git a/deltaspike/src/main/java/baeldung/controller/MemberController.java b/deltaspike/src/main/java/baeldung/controller/MemberController.java new file mode 100644 index 0000000000..7a9e9800f4 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/controller/MemberController.java @@ -0,0 +1,84 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.controller; + +import javax.annotation.PostConstruct; +import javax.enterprise.inject.Model; +import javax.enterprise.inject.Produces; +import javax.faces.application.FacesMessage; +import javax.faces.context.FacesContext; +import javax.inject.Inject; +import javax.inject.Named; + +import baeldung.model.Member; +import baeldung.service.MemberRegistration; + +// The @Model stereotype is a convenience mechanism to make this a request-scoped bean that has an +// EL name +// Read more about the @Model stereotype in this FAQ: +// http://www.cdi-spec.org/faq/#accordion6 +@Model +public class MemberController { + + @Inject + private FacesContext facesContext; + + @Inject + private MemberRegistration memberRegistration; + + @Produces + @Named + private Member newMember; + + @PostConstruct + public void initNewMember() { + newMember = new Member(); + } + + public void register() throws Exception { + try { + memberRegistration.register(newMember); + FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_INFO, "Registered!", "Registration successful"); + facesContext.addMessage(null, m); + initNewMember(); + } catch (Exception e) { + String errorMessage = getRootErrorMessage(e); + FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_ERROR, errorMessage, "Registration unsuccessful"); + facesContext.addMessage(null, m); + } + } + + private String getRootErrorMessage(Exception e) { + // Default to general error message that registration failed. + String errorMessage = "Registration failed. See server log for more information"; + if (e == null) { + // This shouldn't happen, but return the default messages + return errorMessage; + } + + // Start with the exception and recurse to find the root cause + Throwable t = e; + while (t != null) { + // Get the message from the Throwable class instance + errorMessage = t.getLocalizedMessage(); + t = t.getCause(); + } + // This is the root cause message + return errorMessage; + } + +} diff --git a/deltaspike/src/main/java/baeldung/data/EntityManagerProducer.java b/deltaspike/src/main/java/baeldung/data/EntityManagerProducer.java new file mode 100644 index 0000000000..9189dbba74 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/EntityManagerProducer.java @@ -0,0 +1,29 @@ +package baeldung.data; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceUnit; + +@ApplicationScoped +public class EntityManagerProducer { + @PersistenceUnit(unitName = "primary") + private EntityManagerFactory entityManagerFactory; + + @Produces + @Default + @RequestScoped + public EntityManager create() { + return this.entityManagerFactory.createEntityManager(); + } + + public void dispose(@Disposes @Default EntityManager entityManager) { + if (entityManager.isOpen()) { + entityManager.close(); + } + } +} \ No newline at end of file diff --git a/deltaspike/src/main/java/baeldung/data/MemberListProducer.java b/deltaspike/src/main/java/baeldung/data/MemberListProducer.java new file mode 100644 index 0000000000..c1f5fda31d --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/MemberListProducer.java @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.data; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.event.Observes; +import javax.enterprise.event.Reception; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.inject.Named; +import java.util.List; + +import baeldung.model.Member; + +@RequestScoped +public class MemberListProducer { + + @Inject + private MemberRepository memberRepository; + + private List members; + + // @Named provides access the return value via the EL variable name "members" in the UI (e.g. + // Facelets or JSP view) + @Produces + @Named + public List getMembers() { + return members; + } + + public void onMemberListChanged(@Observes(notifyObserver = Reception.IF_EXISTS) final Member member) { + retrieveAllMembersOrderedByName(); + } + + @PostConstruct + public void retrieveAllMembersOrderedByName() { + members = memberRepository.findAllOrderedByNameWithQueryDSL(); + } +} diff --git a/deltaspike/src/main/java/baeldung/data/MemberRepository.java b/deltaspike/src/main/java/baeldung/data/MemberRepository.java new file mode 100644 index 0000000000..56a4a4e634 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/MemberRepository.java @@ -0,0 +1,43 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.data; + +import baeldung.model.Member; +import baeldung.model.QMember; +import org.apache.deltaspike.data.api.*; + +import java.util.List; + +@Repository +@EntityManagerConfig(entityManagerResolver = SecondaryEntityManagerResolver.class) +public abstract class MemberRepository extends AbstractEntityRepository implements QueryDslSupport { + + public abstract Member findById(Long id); + + public abstract Member findByEmail(String email); + + @Query("from Member m order by m.name") + public abstract List findAllOrderedByName(); + + public List findAllOrderedByNameWithQueryDSL() { + final QMember member = QMember.member; + return jpaQuery() + .from(member) + .orderBy(member.email.asc()) + .list(member); + } +} diff --git a/deltaspike/src/main/java/baeldung/data/QueryDslRepositoryExtension.java b/deltaspike/src/main/java/baeldung/data/QueryDslRepositoryExtension.java new file mode 100644 index 0000000000..8cb00958ab --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/QueryDslRepositoryExtension.java @@ -0,0 +1,18 @@ +package baeldung.data; + +import com.mysema.query.jpa.impl.JPAQuery; +import org.apache.deltaspike.data.spi.DelegateQueryHandler; +import org.apache.deltaspike.data.spi.QueryInvocationContext; + +import javax.inject.Inject; + +public class QueryDslRepositoryExtension implements QueryDslSupport, DelegateQueryHandler { + + @Inject + private QueryInvocationContext context; + + @Override + public JPAQuery jpaQuery() { + return new JPAQuery(context.getEntityManager()); + } +} diff --git a/deltaspike/src/main/java/baeldung/data/QueryDslSupport.java b/deltaspike/src/main/java/baeldung/data/QueryDslSupport.java new file mode 100644 index 0000000000..72c33cf1b6 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/QueryDslSupport.java @@ -0,0 +1,7 @@ +package baeldung.data; + +import com.mysema.query.jpa.impl.JPAQuery; + +public interface QueryDslSupport { + JPAQuery jpaQuery(); +} diff --git a/deltaspike/src/main/java/baeldung/data/SecondaryEntityManagerProducer.java b/deltaspike/src/main/java/baeldung/data/SecondaryEntityManagerProducer.java new file mode 100644 index 0000000000..41d30d9018 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/SecondaryEntityManagerProducer.java @@ -0,0 +1,30 @@ +package baeldung.data; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceUnit; + +@ApplicationScoped +public class SecondaryEntityManagerProducer { + @PersistenceUnit(unitName = "secondary") + private EntityManagerFactory entityManagerFactory; + + @Produces + @Default + @RequestScoped + @SecondaryPersistenceUnit + public EntityManager create() { + return this.entityManagerFactory.createEntityManager(); + } + + public void dispose(@Disposes @Default EntityManager entityManager) { + if (entityManager.isOpen()) { + entityManager.close(); + } + } +} \ No newline at end of file diff --git a/deltaspike/src/main/java/baeldung/data/SecondaryEntityManagerResolver.java b/deltaspike/src/main/java/baeldung/data/SecondaryEntityManagerResolver.java new file mode 100644 index 0000000000..7faaa1ac49 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/SecondaryEntityManagerResolver.java @@ -0,0 +1,18 @@ +package baeldung.data; + +import org.apache.deltaspike.data.api.EntityManagerResolver; + +import javax.inject.Inject; +import javax.persistence.EntityManager; + +public class SecondaryEntityManagerResolver implements EntityManagerResolver { + + @Inject + @SecondaryPersistenceUnit + private EntityManager entityManager; + + @Override + public EntityManager resolveEntityManager() { + return entityManager; + } +} diff --git a/deltaspike/src/main/java/baeldung/data/SecondaryPersistenceUnit.java b/deltaspike/src/main/java/baeldung/data/SecondaryPersistenceUnit.java new file mode 100644 index 0000000000..e60d0aff05 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/data/SecondaryPersistenceUnit.java @@ -0,0 +1,12 @@ +package baeldung.data; + +import javax.inject.Qualifier; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Qualifier +public @interface SecondaryPersistenceUnit { +} diff --git a/deltaspike/src/main/java/baeldung/model/Member.java b/deltaspike/src/main/java/baeldung/model/Member.java new file mode 100644 index 0000000000..e178dcf63a --- /dev/null +++ b/deltaspike/src/main/java/baeldung/model/Member.java @@ -0,0 +1,93 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.model; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.constraints.Digits; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import javax.xml.bind.annotation.XmlRootElement; + +import org.hibernate.validator.constraints.Email; +import org.hibernate.validator.constraints.NotEmpty; + +@SuppressWarnings("serial") +@Entity +@XmlRootElement +@Table(uniqueConstraints = @UniqueConstraint(columnNames = "email")) +public class Member implements Serializable { + + @Id + @GeneratedValue + private Long id; + + @NotNull + @Size(min = 1, max = 25) + @Pattern(regexp = "[^0-9]*", message = "Must not contain numbers") + private String name; + + @NotNull + @NotEmpty + @Email + private String email; + + @NotNull + @Size(min = 10, max = 12) + @Digits(fraction = 0, integer = 12) + @Column(name = "phone_number") + private String phoneNumber; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } +} diff --git a/deltaspike/src/main/java/baeldung/rest/JaxRsActivator.java b/deltaspike/src/main/java/baeldung/rest/JaxRsActivator.java new file mode 100644 index 0000000000..9357ae0ea6 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/rest/JaxRsActivator.java @@ -0,0 +1,33 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.rest; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +/** + * A class extending {@link Application} and annotated with @ApplicationPath is the Java EE 7 "no XML" approach to activating + * JAX-RS. + *

+ *

+ * Resources are served relative to the servlet path specified in the {@link ApplicationPath} annotation. + *

+ */ +@ApplicationPath("/rest") +public class JaxRsActivator extends Application { + /* class body intentionally left blank */ +} diff --git a/deltaspike/src/main/java/baeldung/rest/MemberResourceRESTService.java b/deltaspike/src/main/java/baeldung/rest/MemberResourceRESTService.java new file mode 100644 index 0000000000..5a09e45591 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/rest/MemberResourceRESTService.java @@ -0,0 +1,185 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.rest; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.persistence.NoResultException; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.ValidationException; +import javax.validation.Validator; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import baeldung.data.MemberRepository; +import baeldung.model.Member; +import baeldung.service.MemberRegistration; + +/** + * JAX-RS Example + *

+ * This class produces a RESTful service to read/write the contents of the members table. + */ +@Path("/members") +@RequestScoped +public class MemberResourceRESTService { + + @Inject + private Logger log; + + @Inject + private Validator validator; + + @Inject + private MemberRepository repository; + + @Inject + MemberRegistration registration; + + @GET + @Produces(MediaType.APPLICATION_JSON) + public List listAllMembers() { + return repository.findAllOrderedByName(); + } + + @GET + @Path("/{id:[0-9][0-9]*}") + @Produces(MediaType.APPLICATION_JSON) + public Member lookupMemberById(@PathParam("id") long id) { + Member member = repository.findById(id); + if (member == null) { + throw new WebApplicationException(Response.Status.NOT_FOUND); + } + return member; + } + + /** + * Creates a new member from the values provided. Performs validation, and will return a JAX-RS response with either 200 ok, + * or with a map of fields, and related errors. + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response createMember(Member member) { + + Response.ResponseBuilder builder = null; + + try { + // Validates member using bean validation + validateMember(member); + + registration.register(member); + + // Create an "ok" response + builder = Response.ok(); + } catch (ConstraintViolationException ce) { + // Handle bean validation issues + builder = createViolationResponse(ce.getConstraintViolations()); + } catch (ValidationException e) { + // Handle the unique constrain violation + Map responseObj = new HashMap<>(); + responseObj.put("email", "Email taken"); + builder = Response.status(Response.Status.CONFLICT).entity(responseObj); + } catch (Exception e) { + // Handle generic exceptions + Map responseObj = new HashMap<>(); + responseObj.put("error", e.getMessage()); + builder = Response.status(Response.Status.BAD_REQUEST).entity(responseObj); + } + + return builder.build(); + } + + /** + *

+ * Validates the given Member variable and throws validation exceptions based on the type of error. If the error is standard + * bean validation errors then it will throw a ConstraintValidationException with the set of the constraints violated. + *

+ *

+ * If the error is caused because an existing member with the same email is registered it throws a regular validation + * exception so that it can be interpreted separately. + *

+ * + * @param member Member to be validated + * @throws ConstraintViolationException If Bean Validation errors exist + * @throws ValidationException If member with the same email already exists + */ + private void validateMember(Member member) throws ConstraintViolationException, ValidationException { + // Create a bean validator and check for issues. + Set> violations = validator.validate(member); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(new HashSet>(violations)); + } + + // Check the uniqueness of the email address + if (emailAlreadyExists(member.getEmail())) { + throw new ValidationException("Unique Email Violation"); + } + } + + /** + * Creates a JAX-RS "Bad Request" response including a map of all violation fields, and their message. This can then be used + * by clients to show violations. + * + * @param violations A set of violations that needs to be reported + * @return JAX-RS response containing all violations + */ + private Response.ResponseBuilder createViolationResponse(Set> violations) { + log.fine("Validation completed. violations found: " + violations.size()); + + Map responseObj = new HashMap<>(); + + for (ConstraintViolation violation : violations) { + responseObj.put(violation.getPropertyPath().toString(), violation.getMessage()); + } + + return Response.status(Response.Status.BAD_REQUEST).entity(responseObj); + } + + /** + * Checks if a member with the same email address is already registered. This is the only way to easily capture the + * "@UniqueConstraint(columnNames = "email")" constraint from the Member class. + * + * @param email The email to check + * @return True if the email already exists, and false otherwise + */ + public boolean emailAlreadyExists(String email) { + Member member = null; + try { + member = repository.findByEmail(email); + } catch (NoResultException e) { + // ignore + } + return member != null; + } +} diff --git a/deltaspike/src/main/java/baeldung/service/MemberRegistration.java b/deltaspike/src/main/java/baeldung/service/MemberRegistration.java new file mode 100644 index 0000000000..ec65471622 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/service/MemberRegistration.java @@ -0,0 +1,85 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.service; + +import baeldung.data.MemberRepository; +import baeldung.data.SecondaryPersistenceUnit; +import baeldung.model.Member; +import baeldung.model.QMember; + +import javax.ejb.Stateless; +import javax.enterprise.event.Event; +import javax.enterprise.inject.Default; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.ValidationException; +import javax.validation.Validator; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Logger; + +@Stateless +public class MemberRegistration { + + @Inject + private Logger log; + + @Inject + private MemberRepository repository; + + @Inject + private Event memberEventSrc; + + @Inject + private Validator validator; + + private void validateMember(Member member) throws ConstraintViolationException, ValidationException { + // Create a bean validator and check for issues. + Set> violations = validator.validate(member); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(new HashSet>(violations)); + } + + // Check the uniqueness of the email address + if (emailAlreadyExists(member.getEmail())) { + throw new ValidationException("Unique Email Violation"); + } + } + + + public void register(Member member) throws Exception { + log.info("Registering " + member.getName()); + validateMember(member); + repository.save(member); + memberEventSrc.fire(member); + } + + public boolean emailAlreadyExists(String email) { + Member member = null; + try { + member = repository.findByEmail(email); + } catch (NoResultException e) { + // ignore + } + return member != null; + } + +} diff --git a/deltaspike/src/main/java/baeldung/util/Resources.java b/deltaspike/src/main/java/baeldung/util/Resources.java new file mode 100644 index 0000000000..2cc1f235d7 --- /dev/null +++ b/deltaspike/src/main/java/baeldung/util/Resources.java @@ -0,0 +1,53 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.util; + +import java.util.logging.Logger; + +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.faces.context.FacesContext; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +/** + * This class uses CDI to alias Java EE resources, such as the persistence context, to CDI beans + *

+ *

+ * Example injection on a managed bean field: + *

+ *

+ *

+ * @Inject
+ * private EntityManager em;
+ * 
+ */ +public class Resources { + + @Produces + public Logger produceLog(InjectionPoint injectionPoint) { + return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); + } + + @Produces + @RequestScoped + public FacesContext produceFacesContext() { + return FacesContext.getCurrentInstance(); + } + +} diff --git a/deltaspike/src/main/resources/META-INF/apache-deltaspike.properties b/deltaspike/src/main/resources/META-INF/apache-deltaspike.properties new file mode 100644 index 0000000000..a861ad729a --- /dev/null +++ b/deltaspike/src/main/resources/META-INF/apache-deltaspike.properties @@ -0,0 +1 @@ +globalAlternatives.org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy=org.apache.deltaspike.jpa.impl.transaction.BeanManagedUserTransactionStrategy diff --git a/deltaspike/src/main/resources/META-INF/persistence.xml b/deltaspike/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000000..b68c2c1bb1 --- /dev/null +++ b/deltaspike/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,21 @@ + + + + java:jboss/datasources/baeldung-jee7-seedDS + + + + + + + java:jboss/datasources/baeldung-jee7-seed-secondaryDS + + + + + + diff --git a/deltaspike/src/main/resources/import.sql b/deltaspike/src/main/resources/import.sql new file mode 100644 index 0000000000..154f4e9923 --- /dev/null +++ b/deltaspike/src/main/resources/import.sql @@ -0,0 +1,19 @@ +-- +-- JBoss, Home of Professional Open Source +-- Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual +-- contributors by the @authors tag. See the copyright.txt in the +-- distribution for a full listing of individual contributors. +-- +-- Licensed 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. +-- + +-- You can use this file to load seed data into the database using SQL statements +insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') diff --git a/deltaspike/src/main/webapp/WEB-INF/baeldung-jee7-seed-ds.xml b/deltaspike/src/main/webapp/WEB-INF/baeldung-jee7-seed-ds.xml new file mode 100644 index 0000000000..1a8e350667 --- /dev/null +++ b/deltaspike/src/main/webapp/WEB-INF/baeldung-jee7-seed-ds.xml @@ -0,0 +1,37 @@ + + + + + + + jdbc:h2:mem:baeldung-jee7-seed;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1 + h2 + + sa + sa + + + + diff --git a/deltaspike/src/main/webapp/WEB-INF/baeldung-jee7-seed-secondary-ds.xml b/deltaspike/src/main/webapp/WEB-INF/baeldung-jee7-seed-secondary-ds.xml new file mode 100644 index 0000000000..c4c9afb2e0 --- /dev/null +++ b/deltaspike/src/main/webapp/WEB-INF/baeldung-jee7-seed-secondary-ds.xml @@ -0,0 +1,19 @@ + + + + + + jdbc:h2:mem:baeldung-jee7-seed;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1 + h2 + + sa + sa + + + + diff --git a/deltaspike/src/main/webapp/WEB-INF/beans.xml b/deltaspike/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000000..0090177eb7 --- /dev/null +++ b/deltaspike/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/deltaspike/src/main/webapp/WEB-INF/faces-config.xml b/deltaspike/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000000..26cf98b669 --- /dev/null +++ b/deltaspike/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/deltaspike/src/main/webapp/WEB-INF/templates/default.xhtml b/deltaspike/src/main/webapp/WEB-INF/templates/default.xhtml new file mode 100644 index 0000000000..2f001f8626 --- /dev/null +++ b/deltaspike/src/main/webapp/WEB-INF/templates/default.xhtml @@ -0,0 +1,55 @@ + + + + + baeldung-jee7-seed + + + + +
+
+ +
+
+ + [Template content will be inserted here] + +
+ + +
+
+ diff --git a/deltaspike/src/main/webapp/index.html b/deltaspike/src/main/webapp/index.html new file mode 100644 index 0000000000..dd6c4fb886 --- /dev/null +++ b/deltaspike/src/main/webapp/index.html @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/deltaspike/src/main/webapp/index.xhtml b/deltaspike/src/main/webapp/index.xhtml new file mode 100644 index 0000000000..0f515601be --- /dev/null +++ b/deltaspike/src/main/webapp/index.xhtml @@ -0,0 +1,97 @@ + + + + +

Welcome to WildFly!

+ +
+

You have successfully deployed a Java EE 7 Enterprise + Application.

+

Your application can run on:

+ +
+ + +

Member Registration

+

Enforces annotation-based constraints defined on the + model class.

+ + + + + + + + + + + + + + +

+ + + + +

+
+

Members

+ + No registered members. + + + + Id + #{_member.id} + + + Name + #{_member.name} + + + Email + #{_member.email} + + + Phone # + #{_member.phoneNumber} + + + REST URL + /rest/members/#{_member.id} + + + REST URL for all members: /rest/members + + +
+
diff --git a/deltaspike/src/main/webapp/resources/css/screen.css b/deltaspike/src/main/webapp/resources/css/screen.css new file mode 100644 index 0000000000..0e81d5a9d2 --- /dev/null +++ b/deltaspike/src/main/webapp/resources/css/screen.css @@ -0,0 +1,202 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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. + */ +/* Core styles for the page */ +body { + margin: 0; + padding: 0; + background-color: #F1F1F1; + font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; + font-size: 0.8em; + color:#363636; +} + +#container { + margin: 0 auto; + padding: 0 20px 10px 20px; + border-top: 5px solid #000000; + border-left: 5px solid #8c8f91; + border-right: 5px solid #8c8f91; + border-bottom: 25px solid #8c8f91; + width: 865px; /* subtract 40px from banner width for padding */ + background: #FFFFFF; + background-image: url(#{request.contextPath}/resources/gfx/headerbkg.png); + background-repeat: repeat-x; + padding-top: 30px; + box-shadow: 3px 3px 15px #d5d5d5; +} +#content { + float: left; + width: 500px; + margin: 20px; +} +#aside { + font-size: 0.9em; + width: 275px; + float: left; + margin: 20px 0px; + border: 1px solid #D5D5D5; + background: #F1F1F1; + background-image: url(#{request.contextPath}/resources/gfx/asidebkg.png); + background-repeat: repeat-x; + padding: 20px; +} + +#aside ul { + padding-left: 30px; +} +.dualbrand { + float: right; + padding-right: 10px; +} +#footer { + clear: both; + text-align: center; + color: #666666; + font-size: 0.85em; +} +code { + font-size: 1.1em; +} +a { + color: #4a5d75; + text-decoration: none; +} +a:hover { + color: #369; + text-decoration: underline; +} +h1 { + color:#243446; + font-size: 2.25em; +} +h2 { + font-size: 1em; +} +h3 { + color:#243446; +} +h4 { +} +h5 { +} +h6 { +} +/* Member registration styles */ +span.invalid { + padding-left: 3px; + color: red; +} +form { + padding: 1em; + font: 80%/1 sans-serif; + width: 375px; + border: 1px solid #D5D5D5; +} +label { + float: left; + width: 15%; + margin-left: 20px; + margin-right: 0.5em; + padding-top: 0.2em; + text-align: right; + font-weight: bold; + color:#363636; +} +input { + margin-bottom: 8px; +} +.register { + float: left; + margin-left: 85px; +} + +/* ----- table style ------- */ + + +/* = Simple Table style (black header, grey/white stripes */ + +.simpletablestyle { + background-color:#E6E7E8; + clear:both; + width: 550px; +} + +.simpletablestyle img { + border:0px; +} + +.simpletablestyle td { + height:2em; + padding-left: 6px; + font-size:11px; + padding:5px 5px; +} + +.simpletablestyle th { + background: url(#{request.contextPath}/resources/gfx/bkg-blkheader.png) black repeat-x top left; + font-size:12px; + font-weight:normal; + padding:0 10px 0 5px; + border-bottom:#999999 dotted 1px; +} + +.simpletablestyle thead { + background: url(#{request.contextPath}/resources/gfx/bkg-blkheader.png) black repeat-x top left; + height:31px; + font-size:10px; + font-weight:bold; + color:#FFFFFF; + text-align:left; +} + +.simpletablestyle .header a { + color:#94aebd; +} + +.simpletablestype tfoot { + height: 20px; + font-size: 10px; + font-weight: bold; + background-color: #EAECEE; + text-align: center; +} + +.simpletablestyle tr.header td { + padding: 0px 10px 0px 5px; +} + + +.simpletablestyle .subheader { + background-color: #e6e7e8; + font-size:10px; + font-weight:bold; + color:#000000; + text-align:left; +} + +/* Using new CSS3 selectors for styling*/ +.simpletablestyle tr:nth-child(odd) { + background: #f4f3f3; +} +.simpletablestyle tr:nth-child(even) { + background: #ffffff; +} + +.simpletablestyle td a:hover { + color:#3883ce; + text-decoration:none; +} diff --git a/deltaspike/src/main/webapp/resources/gfx/asidebkg.png b/deltaspike/src/main/webapp/resources/gfx/asidebkg.png new file mode 100644 index 0000000000..543d66ad37 Binary files /dev/null and b/deltaspike/src/main/webapp/resources/gfx/asidebkg.png differ diff --git a/deltaspike/src/main/webapp/resources/gfx/bkg-blkheader.png b/deltaspike/src/main/webapp/resources/gfx/bkg-blkheader.png new file mode 100644 index 0000000000..54b95f349f Binary files /dev/null and b/deltaspike/src/main/webapp/resources/gfx/bkg-blkheader.png differ diff --git a/deltaspike/src/main/webapp/resources/gfx/dualbrand_logo.png b/deltaspike/src/main/webapp/resources/gfx/dualbrand_logo.png new file mode 100644 index 0000000000..68853292cb Binary files /dev/null and b/deltaspike/src/main/webapp/resources/gfx/dualbrand_logo.png differ diff --git a/deltaspike/src/main/webapp/resources/gfx/headerbkg.png b/deltaspike/src/main/webapp/resources/gfx/headerbkg.png new file mode 100644 index 0000000000..2669028ba2 Binary files /dev/null and b/deltaspike/src/main/webapp/resources/gfx/headerbkg.png differ diff --git a/deltaspike/src/main/webapp/resources/gfx/wildfly_400x130.jpg b/deltaspike/src/main/webapp/resources/gfx/wildfly_400x130.jpg new file mode 100644 index 0000000000..667c5a66a3 Binary files /dev/null and b/deltaspike/src/main/webapp/resources/gfx/wildfly_400x130.jpg differ diff --git a/deltaspike/src/test/java/baeldung/test/MemberRegistrationTest.java b/deltaspike/src/test/java/baeldung/test/MemberRegistrationTest.java new file mode 100644 index 0000000000..0270d2d164 --- /dev/null +++ b/deltaspike/src/test/java/baeldung/test/MemberRegistrationTest.java @@ -0,0 +1,84 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual + * contributors by the @authors tag. See the copyright.txt in the + * distribution for a full listing of individual contributors. + * + * Licensed 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 baeldung.test; + +import static org.junit.Assert.assertNotNull; + +import java.io.File; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import baeldung.data.*; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import baeldung.model.Member; +import baeldung.service.MemberRegistration; +import baeldung.util.Resources; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class MemberRegistrationTest { + @Deployment + public static Archive createTestArchive() { + File[] files = Maven.resolver().loadPomFromFile("pom.xml") + .importRuntimeDependencies().resolve().withTransitivity().asFile(); + + return ShrinkWrap.create(WebArchive.class, "test.war") + .addClasses( + EntityManagerProducer.class, + Member.class, + MemberRegistration.class, + MemberRepository.class, + Resources.class, + QueryDslRepositoryExtension.class, + QueryDslSupport.class, + SecondaryPersistenceUnit.class, + SecondaryEntityManagerProducer.class, + SecondaryEntityManagerResolver.class) + .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml") + .addAsResource("META-INF/apache-deltaspike.properties") + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") + .addAsWebInfResource("test-ds.xml") + .addAsWebInfResource("test-secondary-ds.xml") + .addAsLibraries(files); + } + + @Inject + MemberRegistration memberRegistration; + + @Inject + Logger log; + + @Test + public void testRegister() throws Exception { + Member newMember = new Member(); + newMember.setName("Jane Doe"); + newMember.setEmail("jane@mailinator.com"); + newMember.setPhoneNumber("2125551234"); + memberRegistration.register(newMember); + assertNotNull(newMember.getId()); + log.info(newMember.getName() + " was persisted with id " + newMember.getId()); + } + +} diff --git a/deltaspike/src/test/resources/META-INF/apache-deltaspike.properties b/deltaspike/src/test/resources/META-INF/apache-deltaspike.properties new file mode 100644 index 0000000000..a861ad729a --- /dev/null +++ b/deltaspike/src/test/resources/META-INF/apache-deltaspike.properties @@ -0,0 +1 @@ +globalAlternatives.org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy=org.apache.deltaspike.jpa.impl.transaction.BeanManagedUserTransactionStrategy diff --git a/deltaspike/src/test/resources/META-INF/test-persistence.xml b/deltaspike/src/test/resources/META-INF/test-persistence.xml new file mode 100644 index 0000000000..bc9fbcbeef --- /dev/null +++ b/deltaspike/src/test/resources/META-INF/test-persistence.xml @@ -0,0 +1,21 @@ + + + + java:jboss/datasources/baeldung-jee7-seedTestDS + + + + + + + java:jboss/datasources/baeldung-jee7-seedTestSecondaryDS + + + + + + diff --git a/deltaspike/src/test/resources/arquillian.xml b/deltaspike/src/test/resources/arquillian.xml new file mode 100644 index 0000000000..14f9e53bbd --- /dev/null +++ b/deltaspike/src/test/resources/arquillian.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + target\wildfly-run\wildfly-10.0.0.Final + + + + diff --git a/deltaspike/src/test/resources/test-ds.xml b/deltaspike/src/test/resources/test-ds.xml new file mode 100644 index 0000000000..8c5d022690 --- /dev/null +++ b/deltaspike/src/test/resources/test-ds.xml @@ -0,0 +1,16 @@ + + + + jdbc:h2:mem:baeldung-jee7-seed-test;DB_CLOSE_DELAY=-1 + h2 + + sa + sa + + + + diff --git a/deltaspike/src/test/resources/test-secondary-ds.xml b/deltaspike/src/test/resources/test-secondary-ds.xml new file mode 100644 index 0000000000..f545adc6d6 --- /dev/null +++ b/deltaspike/src/test/resources/test-secondary-ds.xml @@ -0,0 +1,16 @@ + + + + jdbc:h2:mem:baeldung-jee7-seed-test;DB_CLOSE_DELAY=-1 + h2 + + sa + sa + + + +