1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.orchestra.filter;
21
22 import java.io.IOException;
23
24 import javax.servlet.Filter;
25 import javax.servlet.FilterChain;
26 import javax.servlet.FilterConfig;
27 import javax.servlet.ServletException;
28 import javax.servlet.ServletRequest;
29 import javax.servlet.ServletResponse;
30
31 import org.apache.myfaces.orchestra.connectionManager.ConnectionManagerDataSource;
32 import org.apache.myfaces.orchestra.conversation.ConversationContext;
33 import org.apache.myfaces.orchestra.conversation.ConversationManager;
34
35 /***
36 * Perform a number of useful per-request tasks.
37 * <p>
38 * This filter is entirely optional; it can be omitted if you do not need any of the
39 * functionality provided here.
40 * <p>
41 * Note that it is necessary to define a filter that initialises the Orchestra
42 * framework; this can be done either via a standalone filter such as
43 * JsfFrameworkAdapterFilter, or via a filter that combines framework initialisation
44 * with the functionality of this class, such as
45 * {@link org.apache.myfaces.orchestra.conversation.jsf.filter.OrchestraServletFilter}.
46 * <p>
47 * <h2>Request Serialization</h2>
48 * It is possible for multiple requests associated with the same http session to be
49 * received concurrently.
50 * <p>
51 * By default, the servlet engine simply processes all requests concurrently
52 * in different threads. However that can cause all sorts of unexpected problems with
53 * session-scoped objects.
54 * <p>
55 * The usual solution is to apply a filter to all requests which uses standard java
56 * synchronisation on a session-scoped object, taking the lock on request entry and
57 * releasing it on request exit. This ensures that only one request for that session
58 * runs at a time, with the others waiting until they can obtain a lock on the
59 * appropriate object. This is referred to as "request serialization" because it causes
60 * requests to be processed serially, ie one after the other, rather than concurrently.
61 * This has nothing to do with java.io.Serializable.
62 * <p>
63 * When using an Orchestra conversationContext, session-scoped data should be avoided and
64 * conversation-scoped beans used instead. If there is no session-scoped data in use by
65 * an application then it is possible to allow concurrent requests to the same http session,
66 * but NOT concurrent access to the same orchestra conversationContext. This filter
67 * implements that, by holding a lock on a mutex object held by the appropriate
68 * conversationContext.
69 * <p>
70 * The request serialization functionality can be enabled or disabled via a filter init
71 * parameter; setting "serializeRequests" to "false" disables this feature. Default
72 * value: true (enabled).
73 * <p>
74 * <h2>JDBC Connection Management</h2>
75 * Orchestra provides a special DataSource wrapper that can be configured for any
76 * datasource used by the application. The datasource simply wraps all real Connection
77 * objects it returns in a proxy, and keeps track of them. Here in this servlet it
78 * checks whether all connections borrowed by this thread have been returned, and if
79 * not returns the real connection nulls out the connection held by the proxy. See
80 * ConnectionManagerDataSource for more details.
81 */
82 public class OrchestraServletFilter implements Filter
83 {
84 /***
85 * This filter init property can be set to "true" or "false". Default: "true".
86 */
87 public final static String SERIALIZE_REQUESTS = "serializeRequests";
88
89 /***
90 * Key of an attribute within the ConversationContext that is used as a lock to serialize
91 * multiple http requests for the same conversation context.
92 */
93 private final static String CONTEXT_MUTEXT_OBJECT = OrchestraServletFilter.class.getName() + ".SER_MUTEX";
94
95 private boolean serializeRequests = true;
96
97 public void init(FilterConfig filterConfig) throws ServletException
98 {
99 String value = filterConfig.getInitParameter(SERIALIZE_REQUESTS);
100 if ("false".equals(value))
101 {
102 serializeRequests = false;
103 }
104 }
105
106 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException
107 {
108 try
109 {
110 Object mutex = null;
111 if (serializeRequests)
112 {
113 ConversationManager manager = ConversationManager.getInstance(false);
114 if (manager != null)
115 {
116 ConversationContext conversationContext = manager.getCurrentConversationContext();
117 if (conversationContext != null)
118 {
119 synchronized(conversationContext)
120 {
121 mutex = conversationContext.getAttribute(CONTEXT_MUTEXT_OBJECT);
122 if (mutex == null)
123 {
124 mutex = new Object();
125 conversationContext.setAttribute(CONTEXT_MUTEXT_OBJECT, mutex);
126 }
127 }
128 }
129 }
130 }
131 if (serializeRequests && mutex != null)
132 {
133 synchronized(mutex)
134 {
135 filterChain.doFilter(servletRequest, servletResponse);
136 }
137 }
138 else
139 {
140 filterChain.doFilter(servletRequest, servletResponse);
141 }
142 }
143 finally
144 {
145 cleanupPersistence();
146 }
147 }
148
149 protected void cleanupPersistence()
150 {
151 ConnectionManagerDataSource.releaseAllBorrowedConnections();
152 }
153
154 public void destroy()
155 {
156 }
157 }