1 /*
2 * Copyright 1999,2004-2005 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.catalina.cluster.tcp;
18
19 import java.io.IOException;
20 import java.util.StringTokenizer;
21 import java.util.regex.Pattern;
22
23 import javax.servlet.ServletException;
24
25 import org.apache.catalina.Manager;
26 import org.apache.catalina.Session;
27 import org.apache.catalina.cluster.CatalinaCluster;
28 import org.apache.catalina.cluster.ClusterManager;
29 import org.apache.catalina.cluster.ClusterMessage;
30 import org.apache.catalina.cluster.ClusterSession;
31 import org.apache.catalina.cluster.ClusterValve;
32 import org.apache.catalina.cluster.session.DeltaManager;
33 import org.apache.catalina.connector.Request;
34 import org.apache.catalina.connector.Response;
35 import org.apache.catalina.util.StringManager;
36 import org.apache.catalina.valves.ValveBase;
37
38 /**
39 * <p>Implementation of a Valve that logs interesting contents from the
40 * specified Request (before processing) and the corresponding Response
41 * (after processing). It is especially useful in debugging problems
42 * related to headers and cookies.</p>
43 *
44 * <p>This Valve may be attached to any Container, depending on the granularity
45 * of the logging you wish to perform.</p>
46 *
47 * <p>primaryIndicator=true, then the request attribute <i>org.apache.catalina.cluster.tcp.isPrimarySession.</i>
48 * is set true, when request processing is at sessions primary node.
49 * </p>
50 *
51 * @author Craig R. McClanahan
52 * @author Filip Hanik
53 * @author Peter Rossbach
54 * @version $Revision: 304032 $ $Date: 2005-07-27 11:11:55 -0400 (Wed, 27 Jul 2005) $
55 */
56
57 public class ReplicationValve
58 extends ValveBase implements ClusterValve {
59
60 private static org.apache.commons.logging.Log log =
61 org.apache.commons.logging.LogFactory.getLog( ReplicationValve.class );
62
63 // ----------------------------------------------------- Instance Variables
64
65 /**
66 * The descriptive information related to this implementation.
67 */
68 private static final String info =
69 "org.apache.catalina.cluster.tcp.ReplicationValve/1.2";
70
71
72 /**
73 * The StringManager for this package.
74 */
75 protected static StringManager sm =
76 StringManager.getManager(Constants.Package);
77
78 private CatalinaCluster cluster = null ;
79
80 /**
81 * holds file endings to not call for like images and others
82 */
83 protected java.util.regex.Pattern[] reqFilters = new java.util.regex.Pattern[0];
84 protected String filter ;
85
86 protected long totalRequestTime=0;
87 protected long totalSendTime=0;
88 protected long nrOfRequests =0;
89 protected long lastSendTime =0;
90 protected long nrOfFilterRequests=0;
91 protected boolean primaryIndicator = false ;
92 protected String primaryIndicatorName = "org.apache.catalina.cluster.tcp.isPrimarySession";
93
94 // ------------------------------------------------------------- Properties
95
96 public ReplicationValve() {
97 }
98 /**
99 * Return descriptive information about this Valve implementation.
100 */
101 public String getInfo() {
102
103 return (info);
104
105 }
106
107 /**
108 * @return Returns the cluster.
109 */
110 public CatalinaCluster getCluster() {
111 return cluster;
112 }
113
114 /**
115 * @param cluster The cluster to set.
116 */
117 public void setCluster(CatalinaCluster cluster) {
118 this.cluster = cluster;
119 }
120
121 /**
122 * @return Returns the filter
123 */
124 public String getFilter() {
125 return filter ;
126 }
127
128 /**
129 * compile filter string to regular expressions
130 * @see Pattern#compile(java.lang.String)
131 * @param filter
132 * The filter to set.
133 */
134 public void setFilter(String filter) {
135 if (log.isDebugEnabled())
136 log.debug(sm.getString("ReplicationValve.filter.loading", filter));
137 this.filter = filter;
138 StringTokenizer t = new StringTokenizer(filter, ";");
139 this.reqFilters = new Pattern[t.countTokens()];
140 int i = 0;
141 while (t.hasMoreTokens()) {
142 String s = t.nextToken();
143 if (log.isTraceEnabled())
144 log.trace(sm.getString("ReplicationValve.filter.token", s));
145 try {
146 reqFilters[i++] = Pattern.compile(s);
147 } catch (Exception x) {
148 log.error(sm.getString("ReplicationValve.filter.token.failure",
149 s), x);
150 }
151 }
152 }
153
154 /**
155 * @return Returns the primaryIndicator.
156 */
157 public boolean isPrimaryIndicator() {
158 return primaryIndicator;
159 }
160 /**
161 * @param primaryIndicator The primaryIndicator to set.
162 */
163 public void setPrimaryIndicator(boolean primaryIndicator) {
164 this.primaryIndicator = primaryIndicator;
165 }
166 /**
167 * @return Returns the primaryIndicatorName.
168 */
169 public String getPrimaryIndicatorName() {
170 return primaryIndicatorName;
171 }
172 /**
173 * @param primaryIndicatorName The primaryIndicatorName to set.
174 */
175 public void setPrimaryIndicatorName(String primaryIndicatorName) {
176 this.primaryIndicatorName = primaryIndicatorName;
177 }
178
179 /**
180 * @return Returns the lastSendTime.
181 */
182 public long getLastSendTime() {
183 return lastSendTime;
184 }
185
186 /**
187 * @return Returns the nrOfRequests.
188 */
189 public long getNrOfRequests() {
190 return nrOfRequests;
191 }
192
193 /**
194 * @return Returns the nrOfFilterRequests.
195 */
196 public long getNrOfFilterRequests() {
197 return nrOfFilterRequests;
198 }
199
200 /**
201 * @return Returns the totalRequestTime.
202 */
203 public long getTotalRequestTime() {
204 return totalRequestTime;
205 }
206
207 /**
208 * @return Returns the totalSendTime.
209 */
210 public long getTotalSendTime() {
211 return totalSendTime;
212 }
213
214 /**
215 * @return Returns the reqFilters.
216 */
217 protected java.util.regex.Pattern[] getReqFilters() {
218 return reqFilters;
219 }
220 /**
221 * @param reqFilters The reqFilters to set.
222 */
223 protected void setReqFilters(java.util.regex.Pattern[] reqFilters) {
224 this.reqFilters = reqFilters;
225 }
226
227 // --------------------------------------------------------- Public Methods
228
229
230 /**
231 * Log the interesting request parameters, invoke the next Valve in the
232 * sequence, and log the interesting response parameters.
233 *
234 * @param request The servlet request to be processed
235 * @param response The servlet response to be created
236 *
237 * @exception IOException if an input/output error occurs
238 * @exception ServletException if a servlet error occurs
239 */
240 public void invoke(Request request, Response response)
241 throws IOException, ServletException
242 {
243 long totalstart = System.currentTimeMillis();
244 //this happens before the request
245 if (primaryIndicator)
246 createPrimaryIndicator( request) ;
247 getNext().invoke(request, response);
248 //this happens after the request
249 long start = System.currentTimeMillis();
250 Manager manager = request.getContext().getManager();
251 if (manager != null && manager instanceof ClusterManager) {
252 ClusterManager clusterManager = (ClusterManager) manager;
253 CatalinaCluster cluster = (CatalinaCluster) getContainer()
254 .getCluster();
255 if (cluster == null) {
256 if (log.isWarnEnabled())
257 log.warn(sm.getString("ReplicationValve.nocluster"));
258 return;
259 }
260 // valve cluster can access manager - other clusterhandle replication
261 // at host level - hopefully!
262 if(cluster.getManager(clusterManager.getName()) == null)
263 return ;
264 if(cluster.getMembers().length > 0 ) {
265 try {
266 // send invalid sessions
267 // DeltaManager returns String[0]
268 if (!(clusterManager instanceof DeltaManager))
269 sendInvalidSessions(clusterManager, cluster);
270 // send replication
271 sendSessionReplicationMessage(request, clusterManager, cluster);
272 } catch (Exception x) {
273 log.error(sm.getString("ReplicationValve.send.failure"), x);
274 } finally {
275 long stop = System.currentTimeMillis();
276 updateStats(stop - totalstart, stop - start);
277 }
278 }
279 }
280 }
281
282 /**
283 * reset the active statitics
284 */
285 public void resetStatistics() {
286 totalRequestTime = 0 ;
287 totalSendTime = 0 ;
288 lastSendTime = 0 ;
289 nrOfFilterRequests = 0 ;
290 nrOfRequests = 0 ;
291 }
292
293 /**
294 * Return a String rendering of this object.
295 */
296 public String toString() {
297
298 StringBuffer sb = new StringBuffer("ReplicationValve[");
299 if (container != null)
300 sb.append(container.getName());
301 sb.append("]");
302 return (sb.toString());
303
304 }
305
306 // --------------------------------------------------------- Protected Methods
307
308 /**
309 * Send Cluster Replication Request
310 * @see DeltaManager#requestCompleted(String)
311 * @see SimpleTcpCluster#send(ClusterMessage)
312 * @param request
313 * @param manager
314 * @param cluster
315 */
316 protected void sendSessionReplicationMessage(Request request,
317 ClusterManager manager, CatalinaCluster cluster) {
318 Session session = request.getSessionInternal(false);
319 if (session != null) {
320 String uri = request.getDecodedRequestURI();
321 // request without session change
322 if (!isRequestWithoutSessionChange(uri)) {
323
324 if (log.isDebugEnabled())
325 log.debug(sm.getString("ReplicationValve.invoke.uri", uri));
326 String id = session.getIdInternal();
327 if (id != null) {
328 ClusterMessage msg = manager.requestCompleted(id);
329 // really send replication send request
330 // FIXME send directly via ClusterManager.send
331 if (msg != null) {
332 if(manager.isSendClusterDomainOnly())
333 cluster.sendClusterDomain(msg);
334 else
335 cluster.send(msg);
336 }
337 }
338 } else
339 nrOfFilterRequests++;
340 }
341
342 }
343
344 /**
345 * check for session invalidations
346 * @param manager
347 * @param cluster
348 */
349 protected void sendInvalidSessions(ClusterManager manager, CatalinaCluster cluster) {
350 String[] invalidIds=manager.getInvalidatedSessions();
351 if ( invalidIds.length > 0 ) {
352 for ( int i=0;i<invalidIds.length; i++ ) {
353 try {
354 ClusterMessage imsg = manager.requestCompleted(invalidIds[i]);
355 // FIXME send directly via ClusterManager.send
356 if (imsg != null) {
357 if(manager.isSendClusterDomainOnly())
358 cluster.sendClusterDomain(imsg);
359 else
360 cluster.send(imsg);
361 }
362 } catch ( Exception x ) {
363 log.error(sm.getString("ReplicationValve.send.invalid.failure",invalidIds[i]),x);
364 }
365 }
366 }
367 }
368
369 /**
370 * is request without possible session change
371 * @param uri The request uri
372 * @return True if no session change
373 */
374 protected boolean isRequestWithoutSessionChange(String uri) {
375
376 boolean filterfound = false;
377
378 for (int i = 0; (i < reqFilters.length) && (!filterfound); i++) {
379 java.util.regex.Matcher matcher = reqFilters[i].matcher(uri);
380 filterfound = matcher.matches();
381 }
382 return filterfound;
383 }
384
385 /**
386 * protocol cluster replications stats
387 * @param requestTime
388 * @param clusterTime
389 */
390 protected synchronized void updateStats(long requestTime, long clusterTime) {
391 totalSendTime+=clusterTime;
392 totalRequestTime+=requestTime;
393 nrOfRequests++;
394 if ( (nrOfRequests % 100) == 0 ) {
395 if(log.isInfoEnabled()) {
396 log.info(sm.getString("ReplicationValve.stats",
397 new Object[]{
398 new Long(totalRequestTime/nrOfRequests),
399 new Long(totalSendTime/nrOfRequests),
400 new Long(nrOfRequests),
401 new Long(nrOfFilterRequests),
402 new Long(totalRequestTime),
403 new Long(totalSendTime)}));
404 }
405 }
406 lastSendTime=System.currentTimeMillis();
407 }
408
409
410 /**
411 * Mark Request that processed at primary node with attribute
412 * primaryIndicatorName
413 *
414 * @param request
415 * @throws IOException
416 */
417 protected void createPrimaryIndicator(Request request) throws IOException {
418 String id = request.getRequestedSessionId();
419 if ((id != null) && (id.length() > 0)) {
420 Manager manager = request.getContext().getManager();
421 Session session = manager.findSession(id);
422 if (session instanceof ClusterSession) {
423 ClusterSession cses = (ClusterSession) session;
424 if (cses != null) {
425 Boolean isPrimary = new Boolean(cses.isPrimarySession());
426 if (log.isDebugEnabled())
427 log.debug(sm.getString(
428 "ReplicationValve.session.indicator", request.getContext().getName(),id,
429 primaryIndicatorName, isPrimary));
430 request.setAttribute(primaryIndicatorName, isPrimary);
431 }
432 } else {
433 if (log.isDebugEnabled()) {
434 if (session != null) {
435 log.debug(sm.getString(
436 "ReplicationValve.session.found", request.getContext().getName(),id));
437 } else {
438 log.debug(sm.getString(
439 "ReplicationValve.session.invalid", request.getContext().getName(),id));
440 }
441 }
442 }
443 }
444 }
445
446
447
448 }