From 8cf60e354d0e58585bd8a7ee3855cfc55a34f20b Mon Sep 17 00:00:00 2001 From: "arjan.tijms" Date: Fri, 25 Jul 2014 16:32:28 +0200 Subject: [PATCH 1/2] Basic test for async requests and JASPIC --- jaspic/async-authentication/pom.xml | 26 +++++ .../asyncauthentication/bean/AsyncBean.java | 38 ++++++++ .../sam/SamAutoRegistrationListener.java | 22 +++++ .../sam/TestServerAuthModule.java | 95 +++++++++++++++++++ .../servlet/AsyncServlet.java | 37 ++++++++ .../src/main/webapp/WEB-INF/glassfish-web.xml | 12 +++ .../src/main/webapp/WEB-INF/jboss-web.xml | 5 + .../src/main/webapp/WEB-INF/web.xml | 20 ++++ .../AsyncAuthenticationPublicTest.java | 40 ++++++++ jaspic/pom.xml | 3 + 10 files changed, 298 insertions(+) create mode 100644 jaspic/async-authentication/pom.xml create mode 100644 jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/bean/AsyncBean.java create mode 100644 jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/SamAutoRegistrationListener.java create mode 100644 jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/TestServerAuthModule.java create mode 100644 jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/servlet/AsyncServlet.java create mode 100644 jaspic/async-authentication/src/main/webapp/WEB-INF/glassfish-web.xml create mode 100644 jaspic/async-authentication/src/main/webapp/WEB-INF/jboss-web.xml create mode 100644 jaspic/async-authentication/src/main/webapp/WEB-INF/web.xml create mode 100644 jaspic/async-authentication/src/test/java/org/javaee7/jaspic/asyncauthentication/AsyncAuthenticationPublicTest.java diff --git a/jaspic/async-authentication/pom.xml b/jaspic/async-authentication/pom.xml new file mode 100644 index 000000000..e62c5d34a --- /dev/null +++ b/jaspic/async-authentication/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + org.javaee7.jaspic + jaspic-samples + 1.0-SNAPSHOT + ../pom.xml + + + org.javaee7.jaspic + async-authentication + 1.0-SNAPSHOT + war + + + + org.javaee7.jaspic + common + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/bean/AsyncBean.java b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/bean/AsyncBean.java new file mode 100644 index 000000000..0e36e1f1e --- /dev/null +++ b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/bean/AsyncBean.java @@ -0,0 +1,38 @@ +package org.javaee7.jaspic.asyncauthentication.bean; + +import static java.lang.Thread.interrupted; +import static java.lang.Thread.sleep; + +import java.io.IOException; + +import javax.ejb.Asynchronous; +import javax.ejb.Stateless; +import javax.servlet.AsyncContext; + +/** + * + * @author Arjan Tijms + * + */ +@Stateless +public class AsyncBean { + + @Asynchronous + public void doAsync(AsyncContext asyncContext) { + + try { + sleep(1000); + } catch (InterruptedException e) { + interrupted(); + } + + try { + asyncContext.getResponse().getWriter().write("async response"); + } catch (IOException e) { + e.printStackTrace(); + } + + asyncContext.complete(); + } + +} diff --git a/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/SamAutoRegistrationListener.java b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/SamAutoRegistrationListener.java new file mode 100644 index 000000000..77118e5ac --- /dev/null +++ b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/SamAutoRegistrationListener.java @@ -0,0 +1,22 @@ +package org.javaee7.jaspic.asyncauthentication.sam; + +import javax.servlet.ServletContextEvent; +import javax.servlet.annotation.WebListener; + +import org.javaee7.jaspic.common.BaseServletContextListener; +import org.javaee7.jaspic.common.JaspicUtils; + +/** + * + * @author Arjan Tijms + * + */ +@WebListener +public class SamAutoRegistrationListener extends BaseServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + JaspicUtils.registerSAM(sce.getServletContext(), new TestServerAuthModule()); + } + +} \ No newline at end of file diff --git a/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/TestServerAuthModule.java b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/TestServerAuthModule.java new file mode 100644 index 000000000..79f33a4cc --- /dev/null +++ b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/sam/TestServerAuthModule.java @@ -0,0 +1,95 @@ +package org.javaee7.jaspic.asyncauthentication.sam; + +import static javax.security.auth.message.AuthStatus.SEND_SUCCESS; +import static javax.security.auth.message.AuthStatus.SUCCESS; + +import java.io.IOException; +import java.security.Principal; +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.message.AuthException; +import javax.security.auth.message.AuthStatus; +import javax.security.auth.message.MessageInfo; +import javax.security.auth.message.MessagePolicy; +import javax.security.auth.message.callback.CallerPrincipalCallback; +import javax.security.auth.message.callback.GroupPrincipalCallback; +import javax.security.auth.message.module.ServerAuthModule; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Very basic SAM that returns a single hardcoded user named "test" with role "architect" when the request parameter + * doLogin is present. + * + * @author Arjan Tijms + * + */ +public class TestServerAuthModule implements ServerAuthModule { + + private CallbackHandler handler; + private Class[] supportedMessageTypes = new Class[] { HttpServletRequest.class, HttpServletResponse.class }; + + @Override + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, + @SuppressWarnings("rawtypes") Map options) throws AuthException { + this.handler = handler; + } + + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) + throws AuthException { + + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + + Callback[] callbacks; + + if (request.getParameter("doLogin") != null) { + + // For the test perform a login by directly "returning" the details of the authenticated user. + // Normally credentials would be checked and the details fetched from some repository + + callbacks = new Callback[] { + // The name of the authenticated user + new CallerPrincipalCallback(clientSubject, "test"), + // the roles of the authenticated user + new GroupPrincipalCallback(clientSubject, new String[] { "architect" }) + }; + } else { + + // The JASPIC protocol for "do nothing" + callbacks = new Callback[] { new CallerPrincipalCallback(clientSubject, (Principal) null) }; + } + + try { + + // Communicate the details of the authenticated user to the container. In many + // cases the handler will just store the details and the container will actually handle + // the login after we return from this method. + handler.handle(callbacks); + + } catch (IOException | UnsupportedCallbackException e) { + throw (AuthException) new AuthException().initCause(e); + } + + return SUCCESS; + } + + @Override + public Class[] getSupportedMessageTypes() { + return supportedMessageTypes; + } + + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + return SEND_SUCCESS; + } + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + + } +} \ No newline at end of file diff --git a/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/servlet/AsyncServlet.java b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/servlet/AsyncServlet.java new file mode 100644 index 000000000..d28832d8e --- /dev/null +++ b/jaspic/async-authentication/src/main/java/org/javaee7/jaspic/asyncauthentication/servlet/AsyncServlet.java @@ -0,0 +1,37 @@ +package org.javaee7.jaspic.asyncauthentication.servlet; + +import java.io.IOException; + +import javax.ejb.EJB; +import javax.servlet.AsyncContext; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.javaee7.jaspic.asyncauthentication.bean.AsyncBean; + +/** + * + * @author Arjan Tijms + * + */ +@WebServlet(urlPatterns = "/public/asyncServlet", asyncSupported = true) +public class AsyncServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @EJB + private AsyncBean asyncBean; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + AsyncContext asyncContext = request.startAsync(); + asyncContext.setTimeout(5000); + + asyncBean.doAsync(asyncContext); + } + +} \ No newline at end of file diff --git a/jaspic/async-authentication/src/main/webapp/WEB-INF/glassfish-web.xml b/jaspic/async-authentication/src/main/webapp/WEB-INF/glassfish-web.xml new file mode 100644 index 000000000..26559e3f6 --- /dev/null +++ b/jaspic/async-authentication/src/main/webapp/WEB-INF/glassfish-web.xml @@ -0,0 +1,12 @@ + + + + + + architect + architect + + + + + \ No newline at end of file diff --git a/jaspic/async-authentication/src/main/webapp/WEB-INF/jboss-web.xml b/jaspic/async-authentication/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100644 index 000000000..b6ab7d0ba --- /dev/null +++ b/jaspic/async-authentication/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,5 @@ + + + + jaspitest + diff --git a/jaspic/async-authentication/src/main/webapp/WEB-INF/web.xml b/jaspic/async-authentication/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..ffd58ffa6 --- /dev/null +++ b/jaspic/async-authentication/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,20 @@ + + + + + + Test + /protected/* + + + architect + + + + + architect + + + \ No newline at end of file diff --git a/jaspic/async-authentication/src/test/java/org/javaee7/jaspic/asyncauthentication/AsyncAuthenticationPublicTest.java b/jaspic/async-authentication/src/test/java/org/javaee7/jaspic/asyncauthentication/AsyncAuthenticationPublicTest.java new file mode 100644 index 000000000..89aadca4c --- /dev/null +++ b/jaspic/async-authentication/src/test/java/org/javaee7/jaspic/asyncauthentication/AsyncAuthenticationPublicTest.java @@ -0,0 +1,40 @@ +package org.javaee7.jaspic.asyncauthentication; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.javaee7.jaspic.common.ArquillianBase; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.xml.sax.SAXException; + +/** + * + * + * @author Arjan Tijms + * + */ +@RunWith(Arquillian.class) +public class AsyncAuthenticationPublicTest extends ArquillianBase { + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return defaultArchive(); + } + + /** + * This tests that an async response works at all in the mere presence of + * a JASPIC SAM (that does nothing) + */ + @Test + public void testBasicAsync() throws IOException, SAXException { + + String response = getFromServerPath("public/asyncServlet"); + assertTrue(response.contains("async response")); + } + +} \ No newline at end of file diff --git a/jaspic/pom.xml b/jaspic/pom.xml index f58877d5f..f57a325eb 100644 --- a/jaspic/pom.xml +++ b/jaspic/pom.xml @@ -19,6 +19,9 @@ common + + + async-authentication basic-authentication From bd9e098e254ed4c4e2dca82072a896a523799ef1 Mon Sep 17 00:00:00 2001 From: arjan tijms Date: Sun, 5 Oct 2014 00:52:22 +0200 Subject: [PATCH 2/2] Tests for the @OrderColumn annotation --- jpa/ordercolumn/pom.xml | 14 ++ .../entity/bidirectionaljoin/Child.java | 38 ++++ .../entity/bidirectionaljoin/Parent.java | 45 ++++ .../entity/bidirectionalmappedby/Child.java | 36 ++++ .../entity/bidirectionalmappedby/Parent.java | 43 ++++ .../entity/unidirectional/Child.java | 24 +++ .../entity/unidirectional/Parent.java | 45 ++++ .../OrderColumnTesterService.java | 23 +++ .../OrderColumnTesterService.java | 23 +++ .../OrderColumnTesterService.java | 23 +++ .../main/resources/META-INF/persistence.xml | 10 + .../ordercolumn/OrderColumnBiJoinTest.java | 194 ++++++++++++++++++ .../OrderColumnBiMappedByTest.java | 132 ++++++++++++ .../jpa/ordercolumn/OrderColumnUniTest.java | 172 ++++++++++++++++ jpa/pom.xml | 1 + 15 files changed, 823 insertions(+) create mode 100644 jpa/ordercolumn/pom.xml create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Child.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Parent.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Child.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Parent.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Child.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Parent.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionaljoin/OrderColumnTesterService.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionalmappedby/OrderColumnTesterService.java create mode 100644 jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/unidirectional/OrderColumnTesterService.java create mode 100644 jpa/ordercolumn/src/main/resources/META-INF/persistence.xml create mode 100644 jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiJoinTest.java create mode 100644 jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiMappedByTest.java create mode 100644 jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnUniTest.java diff --git a/jpa/ordercolumn/pom.xml b/jpa/ordercolumn/pom.xml new file mode 100644 index 000000000..1192dd790 --- /dev/null +++ b/jpa/ordercolumn/pom.xml @@ -0,0 +1,14 @@ + + 4.0.0 + + org.javaee7.jpa + jpa-samples + 1.0-SNAPSHOT + ../pom.xml + + + ordercolumn + war + + diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Child.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Child.java new file mode 100644 index 000000000..9dd2113f8 --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Child.java @@ -0,0 +1,38 @@ +package org.javaee7.jpa.ordercolumn.entity.bidirectionaljoin; + +import static javax.persistence.GenerationType.IDENTITY; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class Child { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "parent_id", insertable = false, updatable = false) + private Parent parent; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Parent getParent() { + return parent; + } + + public void setParent(Parent parent) { + this.parent = parent; + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Parent.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Parent.java new file mode 100644 index 000000000..ce6c67bd2 --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionaljoin/Parent.java @@ -0,0 +1,45 @@ +package org.javaee7.jpa.ordercolumn.entity.bidirectionaljoin; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.GenerationType.IDENTITY; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; + +@Entity +public class Parent { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @OneToMany(cascade = ALL, fetch = EAGER) + @OrderColumn + @JoinColumn(name = "parent_id") + private List children = new ArrayList<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Child.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Child.java new file mode 100644 index 000000000..555367b4d --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Child.java @@ -0,0 +1,36 @@ +package org.javaee7.jpa.ordercolumn.entity.bidirectionalmappedby; + +import static javax.persistence.GenerationType.IDENTITY; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Child { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @ManyToOne + private Parent parent; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Parent getParent() { + return parent; + } + + public void setParent(Parent parent) { + this.parent = parent; + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Parent.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Parent.java new file mode 100644 index 000000000..197150f82 --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/bidirectionalmappedby/Parent.java @@ -0,0 +1,43 @@ +package org.javaee7.jpa.ordercolumn.entity.bidirectionalmappedby; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.GenerationType.IDENTITY; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; + +@Entity +public class Parent { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @OneToMany(cascade = ALL, fetch = EAGER, mappedBy = "parent") + @OrderColumn + private List children = new ArrayList<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Child.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Child.java new file mode 100644 index 000000000..8a5d2a895 --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Child.java @@ -0,0 +1,24 @@ +package org.javaee7.jpa.ordercolumn.entity.unidirectional; + +import static javax.persistence.GenerationType.IDENTITY; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Child { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Parent.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Parent.java new file mode 100644 index 000000000..25cd1dc89 --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/entity/unidirectional/Parent.java @@ -0,0 +1,45 @@ +package org.javaee7.jpa.ordercolumn.entity.unidirectional; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.GenerationType.IDENTITY; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; + +@Entity +public class Parent { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @OneToMany(cascade = ALL, fetch = EAGER) + @OrderColumn + @JoinColumn + private List children = new ArrayList<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionaljoin/OrderColumnTesterService.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionaljoin/OrderColumnTesterService.java new file mode 100644 index 000000000..b9369305b --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionaljoin/OrderColumnTesterService.java @@ -0,0 +1,23 @@ +package org.javaee7.jpa.ordercolumn.service.bidirectionaljoin; + +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.javaee7.jpa.ordercolumn.entity.bidirectionaljoin.Parent; + +@Stateless +public class OrderColumnTesterService { + + @PersistenceContext + EntityManager entityManager; + + public Parent save(Parent parent) { + return entityManager.merge(parent); + } + + public Parent getParentById(Long id) { + return entityManager.find(Parent.class, id); + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionalmappedby/OrderColumnTesterService.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionalmappedby/OrderColumnTesterService.java new file mode 100644 index 000000000..eba4bff9e --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/bidirectionalmappedby/OrderColumnTesterService.java @@ -0,0 +1,23 @@ +package org.javaee7.jpa.ordercolumn.service.bidirectionalmappedby; + +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.javaee7.jpa.ordercolumn.entity.bidirectionalmappedby.Parent; + +@Stateless +public class OrderColumnTesterService { + + @PersistenceContext + EntityManager entityManager; + + public Parent save(Parent parent) { + return entityManager.merge(parent); + } + + public Parent getParentById(Long id) { + return entityManager.find(Parent.class, id); + } + +} diff --git a/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/unidirectional/OrderColumnTesterService.java b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/unidirectional/OrderColumnTesterService.java new file mode 100644 index 000000000..f6e1d9ed4 --- /dev/null +++ b/jpa/ordercolumn/src/main/java/org/javaee7/jpa/ordercolumn/service/unidirectional/OrderColumnTesterService.java @@ -0,0 +1,23 @@ +package org.javaee7.jpa.ordercolumn.service.unidirectional; + +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.javaee7.jpa.ordercolumn.entity.unidirectional.Parent; + +@Stateless +public class OrderColumnTesterService { + + @PersistenceContext + EntityManager entityManager; + + public Parent save(Parent parent) { + return entityManager.merge(parent); + } + + public Parent getParentById(Long id) { + return entityManager.find(Parent.class, id); + } + +} diff --git a/jpa/ordercolumn/src/main/resources/META-INF/persistence.xml b/jpa/ordercolumn/src/main/resources/META-INF/persistence.xml new file mode 100644 index 000000000..884c25b24 --- /dev/null +++ b/jpa/ordercolumn/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiJoinTest.java b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiJoinTest.java new file mode 100644 index 000000000..516e0b3f9 --- /dev/null +++ b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiJoinTest.java @@ -0,0 +1,194 @@ +package org.javaee7.jpa.ordercolumn; + +import static org.junit.Assert.assertEquals; + +import javax.ejb.EJB; +import javax.persistence.JoinColumn; +import javax.persistence.OrderColumn; + +import org.javaee7.jpa.ordercolumn.entity.bidirectionaljoin.Child; +import org.javaee7.jpa.ordercolumn.entity.bidirectionaljoin.Parent; +import org.javaee7.jpa.ordercolumn.service.bidirectionaljoin.OrderColumnTesterService; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This tests and demonstrates the {@link OrderColumn} annotation when used together with a + * bi-directional parent-child mapping, where the child holds a foreign key to the parent. + * + *

+ * This is a variation on the normal oneToMany mapping using two {@link JoinColumn} annotations + * instead of using the mappedBy attribute. + * + *

+ * In this mapping the position each child has in the parent's list is stored in the + * child table and fully managed by JPA. This means that this position index does + * not explicitly show up in the object model. + * + *

+ * Example SQL DDL (h2 syntax) + *

+ *      create table Parent (
+ *          id bigint generated by default as identity,
+ *          
+ *          primary key (id)
+ *      );
+ *      
+ *      create table Child (
+ *           id bigint generated by default as identity,
+ *           parent_id bigint,
+ *           children_ORDER integer,
+ *           
+ *           primary key (id),
+ *           foreign key (parent_id) references Parent
+ *       );
+ * 
+ * + * + * @author Arjan Tijms + */ +@RunWith(Arquillian.class) +public class OrderColumnBiJoinTest { + + @Deployment + private static Archive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addPackages(true, Child.class.getPackage()) + .addPackages(true, OrderColumnTesterService.class.getPackage()) + .addAsResource("META-INF/persistence.xml"); + } + + @EJB + private OrderColumnTesterService indexColumnTesterService; + + @Test + public void saveInOneGo() { + Parent parent = new Parent(); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + // Reload parent fresh from data source again + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + /** + * Saves a parent instance first, then adds two children instances and saves again. + * + *

+ * Example sequence of insert/update statements that may be generated to accomplish this: + *

+     * insert into Parent (id) values (null)
+     * insert into Child (id) values (null)
+     * insert into Child (id) values (null)
+     * 
+     * update Child set parent_id = 1, children_ORDER = 0 where id = 1 
+     * update Child set parent_id = 1, children_ORDER = 1 where id = 2 
+     * 
+ * + */ + @Test + public void saveParentSeparatelyFirst() { + + Parent parent = indexColumnTesterService.save(new Parent()); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + + @Test + public void saveParentWithOneChildFirst() { + + Parent parent = new Parent(); + Child child1 = new Child(); + child1.setParent(parent); + parent.getChildren().add(child1); + + // Save parent with 1 child in one go + parent = indexColumnTesterService.save(parent); + + Child child2 = new Child(); + child2.setParent(parent); + parent.getChildren().add(child2); + + // Save parent again with second child + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + @Test + public void saveParentWithChildrenThenDeleteOned() { + + Parent parent = new Parent(); + + Child child1 = new Child(); + child1.setParent(parent); + parent.getChildren().add(child1); + + Child child2 = new Child(); + child2.setParent(parent); + parent.getChildren().add(child2); + + Child child3 = new Child(); + child3.setParent(parent); + parent.getChildren().add(child3); + + // Save parent with 3 children + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("3 children added to parent and saved, but after re-loading number of chilren different", + 3, savedParent.getChildren().size() + ); + + // Removing child at position 1 and saving again + savedParent.getChildren().remove(1); + + savedParent = indexColumnTesterService.save(savedParent); + savedParent = indexColumnTesterService.getParentById(savedParent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + +} diff --git a/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiMappedByTest.java b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiMappedByTest.java new file mode 100644 index 000000000..a149b04a6 --- /dev/null +++ b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiMappedByTest.java @@ -0,0 +1,132 @@ +package org.javaee7.jpa.ordercolumn; + +import static org.junit.Assert.assertEquals; + +import javax.ejb.EJB; +import javax.persistence.OrderColumn; + +import org.javaee7.jpa.ordercolumn.entity.bidirectionalmappedby.Child; +import org.javaee7.jpa.ordercolumn.entity.bidirectionalmappedby.Parent; +import org.javaee7.jpa.ordercolumn.service.bidirectionalmappedby.OrderColumnTesterService; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This tests and demonstrates the {@link OrderColumn} annotation when used together with a + * bi-directional parent-child mapping, where the child holds a foreign key to the parent. + * + *

+ * This is the normal oneToMany mapping using the mappedBy attribute. + * Even though this is the most straightforward mapping, because of a mis-interpretation + * in the JPA spec it was believed for years that this mapping did not have to be + * supported (see https://hibernate.atlassian.net/browse/HHH-5732 among others) + * + *

+ * In this mapping the position each child has in the parent's list is stored in the + * child table and fully managed by JPA. This means that this position index does + * not explicitly show up in the object model. + * + *

+ * Example SQL DDL (h2 syntax) + *

+ *      create table Parent (
+ *          id bigint generated by default as identity,
+ *          
+ *          primary key (id)
+ *      );
+ *      
+ *      create table Child (
+ *           id bigint generated by default as identity,
+ *           parent_id bigint,
+ *           children_ORDER integer,
+ *           
+ *           primary key (id),
+ *           foreign key (parent_id) references Parent
+ *       );
+ * 
+ * + * + * @author Arjan Tijms + */ +@RunWith(Arquillian.class) +public class OrderColumnBiMappedByTest { + + @Deployment + private static Archive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addPackages(true, Parent.class.getPackage()) + .addPackages(true, OrderColumnTesterService.class.getPackage()) + .addAsResource("META-INF/persistence.xml"); + } + + @EJB + private OrderColumnTesterService indexColumnTesterService; + + @Test + public void saveInOneGo() { + + Parent parent = new Parent(); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + /** + * Saves a parent instance first, then adds two children instances and saves again. + * + *

+ * Example sequence of insert/update statements that may be generated to accomplish this: + *

+     * insert into Parent (id) values (null)
+     * insert into Child (id, parent_id) values (null, 1) 
+     * insert into Child (id, parent_id) values (null, 1)
+     *
+     * update Child set children_ORDER = 0 where id = 1
+     * update Child set children_ORDER = 1 where id = 2
+     * 
+ * + */ + @Test + public void saveParentSeparatelyFirst() { + + Parent parent = indexColumnTesterService.save(new Parent()); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + +} \ No newline at end of file diff --git a/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnUniTest.java b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnUniTest.java new file mode 100644 index 000000000..67079628d --- /dev/null +++ b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnUniTest.java @@ -0,0 +1,172 @@ +package org.javaee7.jpa.ordercolumn; + +import static org.junit.Assert.assertEquals; + +import javax.ejb.EJB; +import javax.persistence.OrderColumn; + +import org.javaee7.jpa.ordercolumn.entity.unidirectional.Child; +import org.javaee7.jpa.ordercolumn.entity.unidirectional.Parent; +import org.javaee7.jpa.ordercolumn.service.unidirectional.OrderColumnTesterService; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This tests and demonstrates the {@link OrderColumn} annotation when used together with a + * uni-directional parent-child mapping, where the child table holds a foreign key to the parent. + * + *

+ * In this mapping the position each child has in the parent's list is stored in the + * child table and fully managed by JPA. This means that this position index does + * not explicitly show up in the object model. + * + *

+ * Example SQL DDL (h2 syntax) + *

+ *      create table Parent (
+ *          id bigint generated by default as identity,
+ *          
+ *          primary key (id)
+ *      );
+ *      
+ *      create table Child (
+ *           id bigint generated by default as identity,
+ *           children_id bigint,
+ *           children_ORDER integer,
+ *           
+ *           primary key (id),
+ *           foreign key (children_id) references Parent
+ *       );
+ * 
+ * + * + * @author Arjan Tijms + */ +@RunWith(Arquillian.class) +public class OrderColumnUniTest { + + @Deployment + private static Archive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addPackages(true, Child.class.getPackage()) + .addPackages(true, OrderColumnTesterService.class.getPackage()) + .addAsResource("META-INF/persistence.xml"); + } + + @EJB + private OrderColumnTesterService indexColumnTesterService; + + /** + * Saves a parent instance with 2 children in its children collection in one operation. + * + *

+ * Example sequence of insert/update statements that may be generated to accomplish this: + *

+     * insert into Parent (id) values (null)
+     * insert into Child (id) values (null)
+     * insert into Child (id) values (null)
+     *
+     * update Child set children_id = 1, children_ORDER = 0 where id = 1 
+     * update Child set children_id = 1, children_ORDER = 1 where id = 2
+     * 
+ * + */ + @Test + public void saveInOneGo() { + + Parent parent = new Parent(); + Child child1 = new Child(); + Child child2 = new Child(); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + /** + * Saves a parent instance first, then adds two children instances and saves again. + * + *

+ * Example sequence of insert/update statements that may be generated to accomplish this: + *

+     * insert into Parent (id) values (null)
+     * insert into Child (id) values (null)
+     * insert into Child (id) values (null)
+     *
+     * update Child set children_id = 1, children_ORDER = 0 where id = 1 
+     * update Child set children_id = 1, children_ORDER = 1 where id = 2
+     * 
+ * + */ + @Test + public void saveParentSeparatelyFirst() { + + Parent parent = indexColumnTesterService.save(new Parent()); + + Child child1 = new Child(); + Child child2 = new Child(); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + + /** + * Saves a parent with one child in its children collection first, then adds another child and saves again. + * + *

+ * Example sequence of insert/update statements that may be generated to accomplish this: + *

+     * insert into Parent (id) values (null)
+     * insert into Child (id) values (null)
+     * update Child set children_id = 1, children_ORDER = 0 where id = 1
+     * 
+     * insert into Child (id) values (null)
+     * update Child set children_id = 1, children_ORDER = 1 where id = 2
+     * 
+ */ + @Test + public void saveParentWithOneChildFirst() { + + Parent parent = new Parent(); + Child child1 = new Child(); + parent.getChildren().add(child1); + + // Save parent with 1 child in one go + parent = indexColumnTesterService.save(parent); + + Child child2 = new Child(); + parent.getChildren().add(child2); + + // Save parent again with second child + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + +} diff --git a/jpa/pom.xml b/jpa/pom.xml index b5f48b112..2b49e6416 100644 --- a/jpa/pom.xml +++ b/jpa/pom.xml @@ -29,6 +29,7 @@ jndi-context locking-optimistic locking-pessimistic + ordercolumn pu-typesafe schema-gen-metadata schema-gen-scripts