KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > netui > tools > testrecorder > server > TestRecorderFilter


1 /*
2  * Copyright 2004 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  * $Header:$
17  */

18
19 package org.apache.beehive.netui.tools.testrecorder.server;
20
21 import javax.servlet.Filter JavaDoc;
22 import javax.servlet.FilterChain JavaDoc;
23 import javax.servlet.FilterConfig JavaDoc;
24 import javax.servlet.ServletRequest JavaDoc;
25 import javax.servlet.ServletResponse JavaDoc;
26 import javax.servlet.ServletException JavaDoc;
27 import javax.servlet.http.HttpServletRequest JavaDoc;
28 import javax.servlet.http.HttpServletResponse JavaDoc;
29 import javax.servlet.http.Cookie JavaDoc;
30
31 import java.io.IOException JavaDoc;
32 import java.io.Writer JavaDoc;
33 import java.io.OutputStreamWriter JavaDoc;
34 import java.text.MessageFormat JavaDoc;
35
36 import org.apache.beehive.netui.tools.testrecorder.shared.Constants;
37 import org.apache.beehive.netui.tools.testrecorder.shared.Logger;
38 import org.apache.beehive.netui.tools.testrecorder.shared.xmlbeans.XMLHelper;
39 import org.apache.beehive.netui.tools.testrecorder.shared.config.TestDefinitions;
40 import org.apache.beehive.netui.tools.testrecorder.shared.config.WebappConfig;
41 import org.apache.beehive.netui.tools.testrecorder.server.state.State;
42 import org.apache.beehive.netui.tools.testrecorder.server.state.SessionFailedException;
43 import org.apache.beehive.netui.tools.testrecorder.server.state.PlaybackSession;
44 import org.apache.beehive.netui.tools.testrecorder.server.serverAdapter.ServerAdapter;
45 import org.apache.beehive.netui.tools.testrecorder.server.serverAdapter.ServerAdapterUtils;
46
47
48 public class TestRecorderFilter implements Filter JavaDoc {
49
50     private static final Logger log = Logger.getInstance( TestRecorderFilter.class );
51     private static final String JavaDoc ADMIN_LINK =
52         "<div id=\"netuiTestRecorder\" style=\"background-color:yellow;color: blue;margin:5;padding:10;border: 1pt solid;\">\n" +
53         "<hr>\n" +
54         "<span style=\"font-weight:bold\">Test Recorder:</span>&nbsp;\n" +
55         "<a HREF=\"{0}\">\n" +
56         "<span style=\"color:{1};font-weight:bold;\">{2}</span> Recording</a>\n" +
57         "<a HREF=\"{3}\">Admin</a>\n" +
58         "</div>";
59     /*
60     private static final String ADMIN_LINK =
61             "<hr><table style=\"background-color: yellow; color: blue\" width=\"100%\">" +
62             "<tr><td>&nbsp;&nbsp;&nbsp;&nbsp;<strong>Test Recorder:&nbsp;</strong>" +
63             "&nbsp;&nbsp;&nbsp;<a HREF=\"{0}\">" +
64             "<b style=\"color: {1}\">{2}</b><strong> Recording</strong></a>" +
65             "&nbsp;&nbsp;&nbsp;<a HREF=\"{3}\"><strong>Admin</strong></a>" +
66             "<td><tr></table></span>";
67     */

68     private static final MessageFormat JavaDoc link = new MessageFormat JavaDoc( ADMIN_LINK );
69     // set in init, returned to callers via a public static method
70
private static TestRecorderFilter instance = null;
71     boolean initFlag = false;
72     // set once on first request
73
private static String JavaDoc contextPath;
74     // these two are dependent on contextPath and are set on first request/
75
private static Object JavaDoc[] startLink;
76     private static Object JavaDoc[] stopLink;
77
78     private TestDefinitions testDefinitions;
79     private WebappConfig webapp;
80     private FilterConfig JavaDoc filterConfig = null;
81     private State state;
82
83
84     public TestRecorderFilter() {
85     }
86
87     public void doFilter( ServletRequest JavaDoc request, ServletResponse JavaDoc response,
88             FilterChain JavaDoc chain ) throws IOException JavaDoc, ServletException JavaDoc {
89         if ( initFlag == false ) {
90             filterInit( request );
91         }
92         String JavaDoc reqURI = ( (HttpServletRequest JavaDoc) request ).getRequestURI();
93         if ( log.isDebugEnabled() ) {
94             log.debug( "\n\t\t**********************************************\n" );
95         }
96         if ( !getState().isTestMode() || !shouldHandleRequest( reqURI ) || isControlRequest( reqURI ) ) {
97             // not in test mode just or not a handled suffix, do the normal stuff ...
98
if ( log.isDebugEnabled() ) {
99                 log.debug( "ignoring request, reqURI( " + reqURI + " ), testMode( " +
100                         getState().isTestMode() + " )" );
101             }
102             chain.doFilter( request, response );
103             return;
104         }
105         if ( log.isInfoEnabled() ) {
106             log.info( "reqURI( " + reqURI + " )" );
107         }
108         if ( log.isDebugEnabled() ) {
109             log.debug( "state( " + getState() + " )" );
110             //reportCookies( ((HttpServletRequest) request).getCookies() );
111
//reportHeaders( RequestData.getHeaders( (HttpServletRequest) request ) );
112
}
113         ServerAdapter serverAdapter = ServerAdapterUtils.getServerAdapter();
114         FilterData data = serverAdapter.genFilterDataInstance( request, response, state );
115         try {
116             data.init();
117         }
118         catch ( SessionFailedException e ) {
119             data.addSessionException( e );
120         }
121         if ( data.isSkipFilter() == true ) {
122             // skip filter work and send the request on
123
if ( log.isDebugEnabled() ) {
124                 log.debug( "skiping filter" );
125             }
126             chain.doFilter( data.getRequest(), data.getResponse() );
127             return;
128         }
129         try {
130             // no container work, session work only
131
preFilter( data, chain );
132         }
133         catch ( SessionFailedException e ) {
134             data.addSessionException( e );
135             log.error( "Pre filter work failed", e );
136         }
137         // filter work
138
if ( log.isDebugEnabled() ) {
139             log.debug( "calling doFilter()" );
140         }
141         // store any exceptions ... after cleanup rethrow exceptions
142
// as necessary.
143
try {
144             chain.doFilter( data.getRequest(), data.getNewResponse() );
145         }
146         catch ( Exception JavaDoc ex ) {
147             data.addTestException( ex );
148             if ( log.isDebugEnabled() ) {
149                 log.debug( "caught filter chain exception( " + ex + " )", ex );
150             }
151         }
152         finally {
153             postFilter( data );
154             if ( log.isDebugEnabled() && data.isNewRequest() ) {
155                 log.debug( "\nreturning, state( " + state + " )" );
156             }
157             else if ( log.isDebugEnabled() ) {
158                 log.debug( "data.isNewRequest()( " + data.isNewRequest() + " )" );
159             }
160         }
161         return;
162     }
163
164     protected void postFilter( FilterData data ) throws IOException JavaDoc, ServletException JavaDoc {
165         if ( log.isDebugEnabled() ) {
166             log.debug( "postFilter START" );
167         }
168         try {
169             if ( data.isPlayback() ) {
170                 postFilterPlayback( data );
171             }
172             else {
173                 String JavaDoc body = null;
174                 if ( data.isNewRequest() || data.isNewRecording() ) {
175                     // the request has been wrapped, get the body
176
try {
177                         if ( data.getTestExceptionCount() > 0 ) {
178                             ( (HttpServletResponse JavaDoc) data.getNewResponse() ).setStatus( 500 );
179                         }
180                         data.setRespData( false );
181                         body = data.getRespData().getBody();
182                     }
183                     catch ( Exception JavaDoc e ) {
184                         data.addSessionException( e );
185                         body = "Failed getting output string from wrapper, exception( " +
186                                 e.getMessage() + " )";
187                         log.error( body, e );
188                         body = "<br/><br/><b>" + body + "</b><br/><br/></html>";
189                     }
190                 }
191                 if ( data.isNewRecording() ) {
192                     // recording and outer or new request
193
if ( log.isDebugEnabled() ) {
194                         log.debug( "filter data( " + data + " )" );
195                     }
196                     postFilterRecord( data, body );
197                 }
198                 else if ( data.isNewRequest() ) {
199                     // not recording or playback but we have still captured the response,
200
// write out the response with links if in test mode
201
writeResponse( (ResponseWrapper) data.getNewResponse(), body, data, getState().isTestMode() );
202                 }
203                 else {
204                     // forward or include
205
if ( log.isDebugEnabled() ) {
206                         log.debug( "forward or include, filter data( " + data + " )" );
207                     }
208                 }
209             }
210         }
211         finally {
212             data.throwTestException();
213             if ( log.isDebugEnabled() ) {
214                 log.debug( "postFilter END" );
215             }
216         }
217     }
218
219     protected void postFilterRecord( FilterData data, String JavaDoc body ) throws IOException JavaDoc, ServletException JavaDoc {
220         // capture record response data
221
if ( log.isDebugEnabled() ) {
222             log.debug( "data.isNewRecording()( " + data.isNewRecording() + " )" );
223         }
224         // write the response to the client, let IOExceptions bubble up as part of normal servlet behavior
225
try {
226             writeResponse( (ResponseWrapper) data.getNewResponse(), body, data, true );
227         }
228         finally {
229             int testNum = 0;
230             try {
231                 if ( data.isTestException() ) {
232                     if ( log.isDebugEnabled() ) {
233                         log.debug( "skipping record end, isTestException(" + data.isTestException() + "), body( " +
234                                 body + " )");
235                     }
236                 }
237                 else {
238                     String JavaDoc resp = data.getRespData().getBody();
239                     // replace the CDATA in the body. We escape the CDATA so we can nest this.
240
int pos = resp.indexOf("<![CDATA[");
241                     if (pos != -1) {
242                         resp = resp.replaceAll("\\Q<![CDATA[\\E","&lt;![CDATA[");
243                         resp = resp.replaceAll("\\Q]]>","]]&gt;");
244                         data.getRespData().setBody(resp);
245                     }
246                     testNum = data.getRecordingSession().endTest( data.getReqData(), data.getRespData() );
247                     if ( log.isDebugEnabled() ) {
248                         log.debug( "testNum( " + testNum + " )" );
249                     }
250                 }
251             }
252             catch ( SessionFailedException e ) {
253                 // do not rethrow exception
254
data.addSessionException( e );
255                 log.error( "Failed to end test(" + testNum + ") for session( " +
256                         data.getRecordingSession().getSessionName() + " )" );
257             }
258             data.clearRecording();
259         }
260     }
261
262     protected void postFilterPlayback( FilterData data ) {
263         // capture playback response data
264
if ( log.isDebugEnabled() ) {
265             log.debug( "postFilterPlayback START" );
266         }
267         if ( !data.isNewRequest() ) {
268             // forward or include ... do nothing
269
if ( log.isDebugEnabled() ) {
270                 log.debug( "data.isNewRequest()( " + data.isNewRequest() + " )" );
271                 log.debug( "postFilterPlayback END" );
272             }
273             return;
274         }
275         if ( log.isDebugEnabled() ) {
276             log.debug( "filter data( " + data + " )" );
277         }
278         // new or outer request obtain response data and diff
279
int testNum = -2;
280         try {
281             if ( data.getTestExceptionCount() > 0 ) {
282                 ( (HttpServletResponse JavaDoc) data.getNewResponse() ).setStatus( 500 );
283             }
284             data.setRespData( true );
285             if ( log.isDebugEnabled() ) {
286                 log.debug( "response body( " + data.getRespData().getBody() + " )" );
287             }
288             if ( data.isTestException() ) {
289                 if ( log.isDebugEnabled() ) {
290                     log.debug( "skipping playback end, isTestException(" + data.isTestException() + ")" );
291                 }
292             }
293             else {
294                 testNum = data.getPlaybackSession().endTest( data );
295             }
296
297             if ( log.isDebugEnabled() ) {
298                 log.debug( "testNum( " + testNum + " )" );
299             }
300         }
301                 // Session exception
302
catch ( Exception JavaDoc ex ) {
303             data.addSessionException( ex );
304             if ( log.isDebugEnabled() ) {
305                 log.debug( "Failed ending playback, exception( " + ex.getMessage() + " )", ex );
306             }
307             // do not rethrow.
308
}
309         finally {
310             if ( testNum >= 0 ) {
311                 setResponseOutcome( data.getResponse(), Constants.PASS );
312             }
313             else {
314                 log.error( "Failed ending test(" + testNum + ")" );
315                 setResponseOutcome( data.getResponse(), Constants.FAIL );
316             }
317             // write the output to client
318
writeResponse( (ResponseWrapper) data.getNewResponse(), data.getRespData().getBody(), data, false );
319         }
320         if ( log.isDebugEnabled() ) {
321             log.debug( "postFilterPlayback END" );
322         }
323     }
324
325     /**
326      * @param data
327      * @param chain
328      * @throws SessionFailedException
329      */

330     protected void preFilter( FilterData data, FilterChain JavaDoc chain ) throws SessionFailedException {
331         if ( log.isDebugEnabled() ) {
332             log.debug( "preFilter START" );
333         }
334         if ( data.getTestId() != null ) {
335             // playback ...
336
if ( log.isDebugEnabled() ) {
337                 log.debug( "filter data( " + data + " )" );
338             }
339             if ( data.isNewRequest() ) {
340                 PlaybackSession session = getState().getPlaybackSession( data.getTestId() );
341                 if ( session == null ) {
342                     String JavaDoc msg = "ERROR: no playback session found for test id( " + data.getTestId() + " )";
343                     log.error( msg );
344                     throw new SessionFailedException( msg );
345                 }
346                 data.setPlaybackSession( session );
347                 if ( data.isTestException() ) {
348                     if ( log.isDebugEnabled() ) {
349                         log.debug( "skipping playback start, isTestException(" + data.isTestException() + ")" );
350                     }
351                 }
352                 else {
353                     boolean started = session.startTest();
354                     // may throw SessionFailedException
355
if ( !started ) {
356                         throw new SessionFailedException( "Playback session failed to start, session( " +
357                                 session.getSessionName() + " )" );
358                     }
359                 }
360             }
361             else {
362                 // this is a forward ... do nothing
363
}
364             if ( log.isDebugEnabled() ) {
365                 log.debug( "filter data( " + data + " )" );
366             }
367         }
368         else {
369             // not playback ... maybe recording ...
370
// we want to check this value once and leave it on for the life of the filter data object
371
// (this method call) so we check and set it in a synchronized block
372
synchronized ( getState() ) {
373                 if ( getState().isRecording() == true ) {
374                     data.setRecordingSession( getState().getRecordingSession() );
375                 }
376             }
377             if ( data.isNewRecording() ) {
378                 if ( log.isDebugEnabled() ) {
379                     log.debug( "filter data.isNewRecording()( " + data.isNewRecording() + " )" );
380                 }
381                 // only start recording the request the first time it is seen,
382
// not on forwards back through the container or test exceptions forwarded back to filter
383
if ( data.isTestException() ) {
384                     if ( log.isDebugEnabled() ) {
385                         log.debug( "skipping record start, isTestException(" + data.isTestException() + ")" );
386                     }
387                 }
388                 else {
389                     data.getRecordingSession().startTest();
390                 }
391             }
392             else {
393                 if ( log.isDebugEnabled() ) {
394                     log.debug( "forward, include or not recording filter data( " + data + " )" );
395                 }
396             }
397         }
398         if ( log.isDebugEnabled() ) {
399             log.debug( "preFilter END" );
400         }
401     }
402
403     public void init( FilterConfig JavaDoc filterConfig ) throws ServletException JavaDoc {
404         this.filterConfig = filterConfig;
405         instance = this;
406         String JavaDoc webappName = filterConfig.getInitParameter( "webapp" );
407         System.out.println( "initializing test recorder enabled webapp( " + webappName + " )" );
408         if ( log.isInfoEnabled() ) {
409             log.info( "webapp name( " + webappName + " )" );
410         }
411         testDefinitions = XMLHelper.getTestDefinitionsInstance( Thread.currentThread().getContextClassLoader() );
412         webapp = testDefinitions.getWebapps().getWebapp( webappName );
413         if ( webapp == null ) {
414             throw new ServletException JavaDoc(
415                     "ERROR: unable to find the webapp config for webapp name( " + webappName + " )" );
416         }
417         if ( log.isInfoEnabled() ) {
418             log.debug( "webapp( " + webapp + " )" );
419         }
420         state = new State();
421         state.setTestMode( webapp.isTestMode() );
422     }
423
424     public void destroy() {
425         if ( log.isInfoEnabled() ) {
426             log.info( "destroying test recorder filter for webapp( " + getWebapp().getName() + " )" );
427         }
428         instance = null;
429         this.filterConfig = null;
430     }
431
432     public State getState() {
433         return state;
434     }
435
436     public static TestRecorderFilter instance() {
437         return instance;
438     }
439
440     protected FilterConfig JavaDoc getFilterConfig() {
441         return filterConfig;
442     }
443
444     public WebappConfig getWebapp() {
445         return webapp;
446     }
447
448     public TestDefinitions getTestDefinitions() {
449         return testDefinitions;
450     }
451
452     private void writeResponse( ResponseWrapper wrapper, String JavaDoc body, FilterData data, boolean addLink ) {
453         if ( log.isDebugEnabled() ) {
454             log.debug( "data.getReqURI()( " + data.getReqURI() + " )" );
455         }
456         try {
457             ServletResponse JavaDoc response = wrapper.getResponse();
458             Writer JavaDoc writer = getWriter( response );
459             // add a link to start or stop recording.
460
if ( addLink ) {
461                 int index = body.lastIndexOf( Constants.BODY_END );
462                 if ( index == -1 ) {
463                     index = body.lastIndexOf( Constants.BODY_END_CAPS );
464                 }
465                 if ( index == -1 ) {
466                     if ( log.isDebugEnabled() ) {
467                         log.debug( "</body> was not found:\nbody(" + body + ")" );
468                     }
469                     // just print what is there
470
writer.write( body );
471                 }
472                 else if ( data.isNewRequest() ) {
473                     writer.write( body.substring( 0, index ) );
474                     // write link
475
Object JavaDoc[] objs = null;
476                     if ( getState().isRecording() ) {
477                         objs = stopLink;
478                     }
479                     else {
480                         objs = startLink;
481                     }
482                     writer.write( Constants.NL + link.format( objs ) + Constants.NL );
483                     writer.write( body.substring( index ) );
484                 }
485                 else {
486                     writer.write( body );
487                 }
488             }
489             else {
490                 writer.write( body );
491             }
492         }
493         catch ( IOException JavaDoc e ) {
494             data.addTestException( e );
495             if ( log.isWarnEnabled() ) {
496                 log.warn( "Failed writing response, exception( " + e.getMessage() + " )" );
497             }
498         }
499     }
500
501     private Writer JavaDoc getWriter( ServletResponse JavaDoc resp ) throws IOException JavaDoc {
502         Writer JavaDoc writer = null;
503         try {
504             writer = resp.getWriter();
505         }
506         catch ( IllegalStateException JavaDoc e ) {
507             // when we get an IllegalStateException then someone accessed the
508
// output stream so we need to create the writer from the output stream
509
if ( log.isDebugEnabled() ) {
510                 log.debug( "Creating a Writer for the response" );
511             }
512             writer = new OutputStreamWriter JavaDoc( resp.getOutputStream() );
513         }
514         return writer;
515     }
516
517     private void filterInit( ServletRequest JavaDoc request ) {
518         // prefix for requests to this webapp
519
contextPath = ( (HttpServletRequest JavaDoc) request ).getContextPath();
520         genStartStopLinks();
521         initFlag = true;
522     }
523
524     protected static void genStartStopLinks() {
525         startLink = new Object JavaDoc[4];
526         stopLink = new Object JavaDoc[4];
527
528         startLink[0] = contextPath + "/testRecorder/startRecord.jsp?" +
529                 Constants.FILTER_SKIP_PARAM + "=true";
530         startLink[1] = Constants.GREEN;
531         startLink[2] = Constants.START;
532         startLink[3] = contextPath + "/testRecorder/index.jsp?" +
533                 Constants.FILTER_SKIP_PARAM + "=true";
534
535         stopLink[0] = contextPath + "/testRecorder?" +
536                 Constants.MODE + "=" + Constants.RECORD + "&" +
537                 Constants.CMD + "=" + Constants.STOP + "&" +
538                 Constants.FILTER_SKIP_PARAM + "=true";
539         stopLink[1] = Constants.RED;
540         stopLink[2] = Constants.STOP;
541         stopLink[3] = contextPath + "/testRecorder/index.jsp?" +
542                 Constants.FILTER_SKIP_PARAM + "=true";
543     }
544
545     // determine if this url should be handled by the filter.
546
private boolean shouldHandleRequest( String JavaDoc reqURI ) {
547         boolean rtnVal = false;
548         // is the request URI to a servlet, no suffix
549
int slash = reqURI.lastIndexOf( "/" );
550         int period = reqURI.lastIndexOf( "." );
551         String JavaDoc suffix = null;
552         if ( slash > period ) {
553             // no period in the last segment of the path, no path suffix.
554
suffix = Constants.EMPTY_STRING;
555         }
556         else {
557             suffix = reqURI.substring( period + 1 );
558         }
559         rtnVal = getWebapp().handleSuffix( suffix );
560         return rtnVal;
561     }
562
563     private boolean isControlRequest( String JavaDoc reqURI ) {
564         if ( reqURI.startsWith( getWebapp().getServletURI() ) ) {
565             return true;
566         }
567         return false;
568     }
569
570     static void setResponseOutcome( ServletResponse JavaDoc response, String JavaDoc value ) {
571         ( (HttpServletResponse JavaDoc) response ).setHeader( Constants.OUTCOME_HEADER, value );
572     }
573
574 // debugging method
575
protected static void reportCookies( Cookie JavaDoc[] cookies ) {
576         if ( cookies != null ) {
577             for ( int i = 0; i < cookies.length; i++ ) {
578                 log.debug( "cookies[" + i + "]( " + cookies[i] + " )" );
579             }
580         }
581         else {
582             log.debug( "cookies( " + cookies + " )" );
583         }
584     }
585
586 }
587
Popular Tags