1   package org.apache.velocity.test;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.    
20   */
21  
22  import java.io.StringWriter;
23  import java.io.Writer;
24  
25  import junit.framework.Test;
26  import junit.framework.TestCase;
27  import junit.framework.TestSuite;
28  
29  import org.apache.velocity.VelocityContext;
30  import org.apache.velocity.app.VelocityEngine;
31  import org.apache.velocity.app.event.EventCartridge;
32  import org.apache.velocity.app.event.MethodExceptionEventHandler;
33  import org.apache.velocity.app.event.NullSetEventHandler;
34  import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
35  import org.apache.velocity.context.Context;
36  import org.apache.velocity.exception.MethodInvocationException;
37  import org.apache.velocity.runtime.RuntimeConstants;
38  import org.apache.velocity.runtime.RuntimeServices;
39  import org.apache.velocity.runtime.log.LogChute;
40  import org.apache.velocity.util.ContextAware;
41  import org.apache.velocity.util.RuntimeServicesAware;
42  
43  /**
44   * Tests event handling for all event handlers except IncludeEventHandler.  This is tested
45   * separately due to its complexity.
46   *
47   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
48   * @version $Id: EventHandlingTestCase.java 463298 2006-10-12 16:10:32Z henning $
49   */
50  public class EventHandlingTestCase
51          extends TestCase
52          implements LogChute
53  {
54      private static String NO_REFERENCE_VALUE =  "<no reference value>";
55      private static String REFERENCE_VALUE =  "<reference value>";
56  
57      private static String logString = null;
58  
59      /**
60       * Default constructor.
61       */
62      public EventHandlingTestCase(String name)
63      {
64          super(name);
65      }
66  
67      public static Test suite ()
68      {
69          return new TestSuite(EventHandlingTestCase.class);
70      }
71  
72      public void testManualEventHandlers()
73              throws Exception
74      {
75          TestEventCartridge te = new TestEventCartridge();
76          /**
77           * Test attaching the event cartridge to the context
78           */
79          VelocityEngine ve = new VelocityEngine();
80          ve.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this);
81          ve.init();
82  
83          /*
84           *  lets make a Context and add the event cartridge
85           */
86  
87          VelocityContext inner = new VelocityContext();
88  
89          /*
90           *  Now make an event cartridge, register all the
91           *  event handlers (at once) and attach it to the
92           *  Context
93           */
94  
95          EventCartridge ec = new EventCartridge();
96          ec.addEventHandler(te);
97          ec.attachToContext( inner );
98  
99          /*
100          *  now wrap the event cartridge - we want to make sure that
101          *  we can do this w/o harm
102          */
103 
104         doTestReferenceInsertionEventHandler1(ve, inner);
105         doTestReferenceInsertionEventHandler2(ve, inner);
106         doTestNullValueEventHandler(ve, inner);
107         doTestSetNullValueEventHandler(ve, inner);
108         doTestMethodExceptionEventHandler1(ve, inner);
109         doTestMethodExceptionEventHandler2(ve, inner);
110     }
111 
112     /**
113      * Test assigning the event handlers via properties
114      */
115 
116     public void testConfigurationEventHandlers()
117             throws Exception
118     {
119         VelocityEngine ve = new VelocityEngine();
120         ve.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this);
121         ve.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, TestEventCartridge.class.getName());
122         ve.setProperty(RuntimeConstants.EVENTHANDLER_NULLSET, TestEventCartridge.class.getName());
123         ve.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, TestEventCartridge.class.getName());
124 
125         ve.init();
126 
127         doTestReferenceInsertionEventHandler1(ve, null);
128         doTestReferenceInsertionEventHandler2(ve, null);
129         doTestNullValueEventHandler(ve, null);
130         doTestSetNullValueEventHandler(ve, null);
131         doTestMethodExceptionEventHandler1(ve, null);
132         doTestMethodExceptionEventHandler2(ve, null);
133     }
134 
135     /**
136      * Test all the event handlers using the given engine.
137      * @param ve
138      * @param vcontext
139      */
140     private void doTestReferenceInsertionEventHandler1(VelocityEngine ve, VelocityContext vc)
141             throws Exception
142     {
143         VelocityContext context = new VelocityContext(vc);
144 
145         context.put("name", "Velocity");
146 
147         /*
148          *  First, the reference insertion handler
149          */
150 
151         String s = "$name$name$name";
152 
153         StringWriter w = new StringWriter();
154         ve.evaluate( context, w, "mystring", s );
155 
156         if ( !w.toString().equals( REFERENCE_VALUE + REFERENCE_VALUE + REFERENCE_VALUE ))
157         {
158             fail( "Reference insertion test 1");
159         }
160     }
161 
162     private void doTestReferenceInsertionEventHandler2(VelocityEngine ve, VelocityContext vc)
163             throws Exception
164     {
165         VelocityContext context = new VelocityContext(vc);
166         context.put("name", "Velocity");
167 
168         /*
169          *  using the same handler, we can deal with
170          *  null references as well
171          */
172 
173         String s = "$floobie";
174 
175         Writer w = new StringWriter();
176         ve.evaluate( context, w, "mystring", s );
177 
178         if ( !w.toString().equals( NO_REFERENCE_VALUE ))
179         {
180             fail( "Reference insertion test 2");
181         }
182     }
183 
184     private void doTestNullValueEventHandler(VelocityEngine ve, VelocityContext vc)
185             throws Exception
186     {
187         VelocityContext context = new VelocityContext(vc);
188 
189         /*
190          *  now lets test setting a null value - this test
191          *  should result in *no* log output.
192          */
193 
194         String s = "#set($settest = $NotAReference)";
195         Writer w = new StringWriter();
196         clearLogString();
197         ve.evaluate( context, w, "mystring", s );
198 
199         if( getLogString() != null)
200         {
201             fail( "NullSetEventHandler test 1");
202         }
203     }
204 
205     private void doTestSetNullValueEventHandler(VelocityEngine ve, VelocityContext vc)
206             throws Exception
207     {
208         VelocityContext context = new VelocityContext(vc);
209 
210         /*
211          *  now lets test setting a null value - this test
212          *  should result in log output.
213          */
214 
215         String s = "#set($logthis = $NotAReference)";
216         Writer w = new StringWriter();
217         clearLogString();
218         ve.evaluate( context, w, "mystring", s );
219 
220         if( getLogString() == null)
221         {
222             fail( "NullSetEventHandler test 2");
223         }
224     }
225 
226     private void doTestMethodExceptionEventHandler1(VelocityEngine ve, VelocityContext vc)
227             throws Exception
228     {
229         VelocityContext context = new VelocityContext(vc);
230 
231         /*
232          *  finally, we test a method exception event - we do this
233          *  by putting this class in the context, and calling
234          *  a method that does nothing but throw an exception.
235          *  we use flag in the context to turn the event handling
236          *  on and off
237          *
238          *  Note also how the reference insertion process
239          *  happens as well
240          */
241 
242         context.put("allow_exception",Boolean.TRUE);
243 
244         context.put("this", this );
245 
246         String s = " $this.throwException()";
247         Writer w = new StringWriter();
248 
249         ve.evaluate( context, w, "mystring", s );
250     }
251 
252 
253     private void doTestMethodExceptionEventHandler2(VelocityEngine ve, VelocityContext vc)
254             throws Exception
255     {
256         VelocityContext context = new VelocityContext(vc);
257         context.put("this", this );
258 
259         /*
260          *  now, we remove the exception flag, and we can see that the
261          *  exception will propgate all the way up here, and
262          *  wil be caught by the catch() block below
263          */
264 
265         String s = " $this.throwException()";
266         Writer w = new StringWriter();
267 
268         try
269         {
270             ve.evaluate( context, w, "mystring", s );
271             fail("No MethodExceptionEvent received!");
272         }
273         catch( MethodInvocationException mee )
274         {
275             // Do nothing
276         }
277     }
278 
279     /**
280      *  silly method to throw an exception to test
281      *  the method invocation exception event handling
282      */
283     public void throwException()
284             throws Exception
285     {
286         throw new Exception("Hello from throwException()");
287     }
288 
289     /**
290      * Required by LogChute
291      */
292     public void init( RuntimeServices rs )
293     {
294         /* don't need it...*/
295     }
296 
297     /**
298      * handler for LogChute interface
299      */
300     public void log(int level, String message)
301     {
302         setLogString(message);
303     }
304 
305     public void log(int level, String message, Throwable t)
306     {
307         setLogString(message);
308     }
309 
310     public boolean isLevelEnabled(int level)
311     {
312         return true;
313     }
314 
315     public static void clearLogString()
316     {
317         logString = null;
318     }
319 
320     public static void setLogString(String message)
321     {
322         logString = message;
323     }
324 
325     public static String getLogString()
326     {
327         return logString;
328     }
329 
330     public static class TestEventCartridge
331             implements ReferenceInsertionEventHandler,
332                        NullSetEventHandler, MethodExceptionEventHandler,
333                        RuntimeServicesAware,ContextAware
334     {
335         private RuntimeServices rs;
336 
337         public TestEventCartridge()
338         {
339         }
340 
341         /**
342          * Required by EventHandler
343          */
344         public void setRuntimeServices( RuntimeServices rs )
345         {
346             // make sure this is only called once
347             if (this.rs == null)
348                 this.rs = rs;
349 
350             else
351                 fail("initialize called more than once.");
352         }
353 
354         /**
355          *  Event handler for when a reference is inserted into the output stream.
356          */
357         public Object referenceInsert( String reference, Object value  )
358         {
359             // as a test, make sure this EventHandler is initialized
360             if (rs == null)
361                 fail ("Event handler not initialized!");
362 
363 
364             /*
365              *  if we have a value
366              *  return a known value
367              */
368             String s = null;
369 
370             if( value != null )
371             {
372                 s = REFERENCE_VALUE;
373             }
374             else
375             {
376                 /*
377                  * we only want to deal with $floobie - anything
378                  *  else we let go
379                  */
380                 if ( reference.equals("$floobie") )
381                 {
382                     s = NO_REFERENCE_VALUE;
383                 }
384             }
385             return s;
386         }
387 
388         /**
389          *  Event handler for when the right hand side of
390          *  a #set() directive is null, which results in
391          *  a log message.  This method gives the application
392          *  a chance to 'vote' on msg generation
393          */
394         public boolean shouldLogOnNullSet( String lhs, String rhs )
395         {
396             // as a test, make sure this EventHandler is initialized
397             if (rs == null)
398                 fail ("Event handler not initialized!");
399 
400             if (lhs.equals("$settest"))
401                 return false;
402 
403             return true;
404         }
405 
406         /**
407          *  Handles exceptions thrown during in-template method access
408          */
409         public Object methodException( Class claz, String method, Exception e )
410                 throws Exception
411         {
412             // as a test, make sure this EventHandler is initialized
413             if (rs == null)
414                 fail ("Event handler not initialized!");
415 
416             // only do processing if the switch is on
417             if (context != null)
418             {
419                 boolean exceptionSwitch = context.containsKey("allow_exception");
420 
421                 if( exceptionSwitch && method.equals("throwException"))
422                 {
423                     return "handler";
424                 }
425                 else
426                     throw e;
427 
428             } else
429 
430                 throw e;
431         }
432 
433         Context context;
434 
435 
436         public void setContext(Context context)
437         {
438             this.context = context;
439         }
440     }
441 }