Added support for HttpSession events
Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/8a424606 Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/8a424606 Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/8a424606 Branch: refs/heads/master Commit: 8a424606874a37395846d117a631e345a910788a Parents: 377fc6e Author: Christian Kaltepoth <[email protected]> Authored: Wed May 22 08:28:39 2013 +0200 Committer: Christian Kaltepoth <[email protected]> Committed: Thu Jun 13 06:53:29 2013 +0200 ---------------------------------------------------------------------- deltaspike/modules/servlet/impl/pom.xml | 7 + .../deltaspike/servlet/impl/EventEmitter.java | 43 +++++ .../servlet/impl/ServletEventBridgeFilter.java | 13 +- .../impl/ServletEventBridgeListener.java | 48 +++++ .../main/resources/META-INF/web-fragment.xml | 5 + .../event/session/CreateSessionServlet.java | 46 +++++ .../event/session/DestroySessionServlet.java | 51 ++++++ .../event/session/SessionEventsObserver.java | 64 +++++++ .../impl/event/session/SessionEventsTest.java | 177 +++++++++++++++++++ 9 files changed, 442 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/pom.xml ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/pom.xml b/deltaspike/modules/servlet/impl/pom.xml index 03ac17c..ee2ae40 100644 --- a/deltaspike/modules/servlet/impl/pom.xml +++ b/deltaspike/modules/servlet/impl/pom.xml @@ -61,6 +61,13 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-impl</artifactId> + <version>1.1.0-beta-1</version> + <scope>test</scope> + </dependency> + </dependencies> </project> http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/EventEmitter.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/EventEmitter.java b/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/EventEmitter.java new file mode 100644 index 0000000..a9af77e --- /dev/null +++ b/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/EventEmitter.java @@ -0,0 +1,43 @@ +/* + * 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.deltaspike.servlet.impl; + +import java.lang.annotation.Annotation; + +import org.apache.deltaspike.core.api.provider.BeanManagerProvider; + +/** + * Base class for classes which send servlet events to the CDI event bus. This class uses {@link BeanManagerProvider} to + * obtain the BeanManager. + * + * @author Christian Kaltepoth + */ +abstract class EventEmitter +{ + + protected void fireEvent(Object event, Annotation... qualifier) + { + /* + * No need to cache the BeanManager reference because the providers already does this on a context class loader + * level. + */ + BeanManagerProvider.getInstance().getBeanManager().fireEvent(event, qualifier); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeFilter.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeFilter.java b/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeFilter.java index f0763dc..90cadd3 100644 --- a/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeFilter.java +++ b/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeFilter.java @@ -19,7 +19,6 @@ package org.apache.deltaspike.servlet.impl; import java.io.IOException; -import java.lang.annotation.Annotation; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -28,7 +27,6 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import org.apache.deltaspike.core.api.provider.BeanManagerProvider; import org.apache.deltaspike.servlet.api.literal.DestroyedLiteral; import org.apache.deltaspike.servlet.api.literal.InitializedLiteral; import org.apache.deltaspike.servlet.api.literal.WebLiteral; @@ -36,7 +34,7 @@ import org.apache.deltaspike.servlet.api.literal.WebLiteral; /** * @author Christian Kaltepoth */ -public class ServletEventBridgeFilter implements Filter +public class ServletEventBridgeFilter extends EventEmitter implements Filter { @Override @@ -71,13 +69,4 @@ public class ServletEventBridgeFilter implements Filter // nothing yet } - protected void fireEvent(Object event, Annotation... qualifier) - { - /* - * No need to cache the BeanManager reference because the providers already does this on a context class loader - * level. - */ - BeanManagerProvider.getInstance().getBeanManager().fireEvent(event, qualifier); - } - } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeListener.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeListener.java b/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeListener.java new file mode 100644 index 0000000..6993351 --- /dev/null +++ b/deltaspike/modules/servlet/impl/src/main/java/org/apache/deltaspike/servlet/impl/ServletEventBridgeListener.java @@ -0,0 +1,48 @@ +/* + * 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.deltaspike.servlet.impl; + +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; + +import org.apache.deltaspike.servlet.api.literal.DestroyedLiteral; +import org.apache.deltaspike.servlet.api.literal.InitializedLiteral; +import org.apache.deltaspike.servlet.api.literal.WebLiteral; + +/** + * This class listens for various servlet events and forwards them to the CDI event bus. + * + * @author Christian Kaltepoth + */ +public class ServletEventBridgeListener extends EventEmitter implements HttpSessionListener +{ + + @Override + public void sessionCreated(HttpSessionEvent se) + { + fireEvent(se.getSession(), WebLiteral.INSTANCE, InitializedLiteral.INSTANCE); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) + { + fireEvent(se.getSession(), WebLiteral.INSTANCE, DestroyedLiteral.INSTANCE); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/main/resources/META-INF/web-fragment.xml ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/main/resources/META-INF/web-fragment.xml b/deltaspike/modules/servlet/impl/src/main/resources/META-INF/web-fragment.xml index 255ae66..af56b7d 100644 --- a/deltaspike/modules/servlet/impl/src/main/resources/META-INF/web-fragment.xml +++ b/deltaspike/modules/servlet/impl/src/main/resources/META-INF/web-fragment.xml @@ -10,6 +10,11 @@ </before> </ordering> + <listener> + <display-name>ServletEventBridgeListener</display-name> + <listener-class>org.apache.deltaspike.servlet.impl.ServletEventBridgeListener</listener-class> + </listener> + <filter> <display-name>ServletEventBridgeFilter</display-name> <filter-name>ServletEventBridgeFilter</filter-name> http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/CreateSessionServlet.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/CreateSessionServlet.java b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/CreateSessionServlet.java new file mode 100644 index 0000000..1644aa1 --- /dev/null +++ b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/CreateSessionServlet.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.deltaspike.test.servlet.impl.event.session; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +/** + * Simple servlet that creates a {@link HttpSession} + * + * @author Christian Kaltepoth + */ +public class CreateSessionServlet extends HttpServlet +{ + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + // force creation of a new session + req.getSession(true); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/DestroySessionServlet.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/DestroySessionServlet.java b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/DestroySessionServlet.java new file mode 100644 index 0000000..bffef69 --- /dev/null +++ b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/DestroySessionServlet.java @@ -0,0 +1,51 @@ +/* + * 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.deltaspike.test.servlet.impl.event.session; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +/** + * Simple servlet that invalidates an existing session. The servlet will throw an {@link IllegalStateException} if no + * session exists. + * + * @author Christian Kaltepoth + */ +public class DestroySessionServlet extends HttpServlet +{ + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + HttpSession session = req.getSession(false); + if (session == null) + { + throw new IllegalStateException("No existing session"); + } + session.invalidate(); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsObserver.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsObserver.java b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsObserver.java new file mode 100644 index 0000000..9db5be6 --- /dev/null +++ b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsObserver.java @@ -0,0 +1,64 @@ +/* + * 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.deltaspike.test.servlet.impl.event.session; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.servlet.http.HttpSession; + +import org.apache.deltaspike.servlet.api.Destroyed; +import org.apache.deltaspike.servlet.api.Initialized; +import org.apache.deltaspike.servlet.api.Web; + +/** + * Application scoped observer which listens for {@link HttpSession} events on the CDI event bus. + * + * @author Christian Kaltepoth + */ +@ApplicationScoped +public class SessionEventsObserver +{ + + private final List<String> eventLog = new ArrayList<String>(); + + public void sessionInitialized(@Observes @Web @Initialized HttpSession session) + { + eventLog.add("Initialized HttpSession"); + } + + public void sessionDestroyed(@Observes @Web @Destroyed HttpSession session) + { + eventLog.add("Destroyed HttpSession"); + } + + public int getEventCount() + { + return eventLog.size(); + } + + public List<String> getEventLog() + { + return Collections.unmodifiableList(eventLog); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/8a424606/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsTest.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsTest.java b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsTest.java new file mode 100644 index 0000000..e855f78 --- /dev/null +++ b/deltaspike/modules/servlet/impl/src/test/java/org/apache/deltaspike/test/servlet/impl/event/session/SessionEventsTest.java @@ -0,0 +1,177 @@ +/* + * 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.deltaspike.test.servlet.impl.event.session; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.URL; + +import javax.inject.Inject; + +import org.apache.deltaspike.test.category.WebProfileCategory; +import org.apache.deltaspike.test.servlet.impl.Deployments; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.params.ClientPNames; +import org.apache.http.client.params.CookiePolicy; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.util.EntityUtils; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.junit.InSequence; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; +import org.jboss.shrinkwrap.descriptor.api.spec.servlet.web.WebAppDescriptor; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +/** + * Test which validates that CDI events are fired when sessions are created and destroyed + * + * @author Christian Kaltepoth + */ +@RunWith(Arquillian.class) +@Category(WebProfileCategory.class) +public class SessionEventsTest +{ + + @Deployment + public static WebArchive getDeployment() + { + return ShrinkWrap.create(WebArchive.class, "test.war") + .addAsLibraries(Deployments.getDeltaSpikeCoreArchives()) + .addAsLibraries(Deployments.getDeltaSpikeServletArchives()) + .addAsLibraries(Deployments.getTestSupportArchives()) + .addClass(SessionEventsObserver.class) + .addAsWebInfResource(new StringAsset("<beans/>"), "beans.xml") + .addAsWebResource(new StringAsset("foobar"), "foobar.txt") + .setWebXML(new StringAsset( + Descriptors.create(WebAppDescriptor.class) + .servlet(CreateSessionServlet.class, "/create-session") + .servlet(DestroySessionServlet.class, "/destroy-session") + .exportAsString())); + + } + + /** + * Needs to be static so that the client can be reused between test methods + */ + private static DefaultHttpClient client; + + @BeforeClass + public static void initHttpClient() + { + client = new DefaultHttpClient(); + client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2109); + } + + @AfterClass + public static void destroyHttpClient() + { + client = null; + } + + @Inject + private SessionEventsObserver observer; + + /** + * First send a request to a standard resource which won't create a user session + */ + @Test + @RunAsClient + @InSequence(1) + public void sendStatelessRequest(@ArquillianResource URL contextPath) throws Exception + { + String url = new URL(contextPath, "foobar.txt").toString(); + HttpResponse response = client.execute(new HttpGet(url)); + assertEquals(200, response.getStatusLine().getStatusCode()); + EntityUtils.consumeQuietly(response.getEntity()); + } + + /** + * The observer didn't any events because no session was created + */ + @Test + @InSequence(2) + public void statelessRequestDoesntEmitEvents() + { + assertEquals(0, observer.getEventCount()); + } + + /** + * Now send a request which creates a session + */ + @Test + @RunAsClient + @InSequence(3) + public void sendRequestToCreateSession(@ArquillianResource URL contextPath) throws Exception + { + String url = new URL(contextPath, "create-session").toString(); + HttpResponse response = client.execute(new HttpGet(url)); + assertEquals(200, response.getStatusLine().getStatusCode()); + EntityUtils.consumeQuietly(response.getEntity()); + } + + /** + * The observer should have received a event + */ + @Test + @InSequence(4) + public void shouldReceiveInitializedSessionEvent() + { + assertEquals(1, observer.getEventCount()); + assertTrue("Didn't receive expected event", + observer.getEventLog().contains("Initialized HttpSession")); + } + + /** + * Now send a request which creates a session + */ + @Test + @RunAsClient + @InSequence(5) + public void sendRequestToDestroySession(@ArquillianResource URL contextPath) throws Exception + { + String url = new URL(contextPath, "destroy-session").toString(); + HttpResponse response = client.execute(new HttpGet(url)); + assertEquals(200, response.getStatusLine().getStatusCode()); + EntityUtils.consumeQuietly(response.getEntity()); + } + + /** + * The observer should have received a event + */ + @Test + @InSequence(6) + public void shouldReceiveDestroyedSessionEvent() + { + assertEquals(2, observer.getEventCount()); + assertTrue("Didn't receive expected event", + observer.getEventLog().contains("Destroyed HttpSession")); + } + +}
