View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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"; // NON-NLS
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)) // NON-NLS
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 }