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  import org.apache.velocity.VelocityContext;
25  import org.apache.velocity.app.VelocityEngine;
26  import org.apache.velocity.app.event.EventCartridge;
27  import org.apache.velocity.app.event.MethodExceptionEventHandler;
28  import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
29  import org.apache.velocity.context.Context;
30  import org.apache.velocity.exception.MethodInvocationException;
31  import org.apache.velocity.runtime.RuntimeConstants;
32  import org.apache.velocity.runtime.RuntimeServices;
33  import org.apache.velocity.util.ContextAware;
34  import org.apache.velocity.util.RuntimeServicesAware;
35  import org.apache.velocity.test.misc.TestLogChute;
36  
37  /**
38   * Tests event handling for all event handlers except IncludeEventHandler.  This is tested
39   * separately due to its complexity.
40   *
41   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
42   * @version $Id: EventHandlingTestCase.java 998264 2010-09-17 19:13:02Z apetrelli $
43   */
44  public class EventHandlingTestCase extends BaseTestCase
45  {
46      private static String NO_REFERENCE_VALUE =  "<no reference value>";
47      private static String REFERENCE_VALUE =  "<reference value>";
48  
49      public EventHandlingTestCase(String name)
50      {
51          super(name);
52      }
53  
54      public void testManualEventHandlers()
55              throws Exception
56      {
57          TestEventCartridge te = new TestEventCartridge();
58          /**
59           * Test attaching the event cartridge to the context.
60           *  Make an event cartridge, register all the
61           *  event handlers (at once) and attach it to the
62           *  Context
63           */
64  
65          EventCartridge ec = new EventCartridge();
66          ec.addEventHandler(te);
67          ec.attachToContext(context);
68  
69          /*
70           *  now wrap the event cartridge - we want to make sure that
71           *  we can do this w/o harm
72           */
73          doTestReferenceInsertionEventHandler1();
74          doTestReferenceInsertionEventHandler2();
75          doTestMethodExceptionEventHandler1();
76          doTestMethodExceptionEventHandler2();
77      }
78  
79      /**
80       * Test assigning the event handlers via properties
81       */
82      public void testConfigurationEventHandlers()
83              throws Exception
84      {
85          engine.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, TestEventCartridge.class.getName());
86          engine.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, TestEventCartridge.class.getName());
87  
88          doTestReferenceInsertionEventHandler1();
89          doTestReferenceInsertionEventHandler2();
90          doTestMethodExceptionEventHandler1();
91          doTestMethodExceptionEventHandler2();
92      }
93  
94      /**
95       * Test all the event handlers using the given engine.
96       */
97      private void doTestReferenceInsertionEventHandler1()
98              throws Exception
99      {
100         VelocityContext outer = context;
101         context = new VelocityContext(context);
102         context.put("name", "Velocity");
103 
104         /*
105          *  First, the reference insertion handler
106          */
107         String expected = REFERENCE_VALUE + REFERENCE_VALUE + REFERENCE_VALUE;
108         assertEvalEquals(expected, "$name$name$name");
109 
110         context = outer;
111     }
112 
113     private void doTestReferenceInsertionEventHandler2()
114             throws Exception
115     {
116         VelocityContext outer = context;
117         context = new VelocityContext(context);
118         context.put("name", "Velocity");
119 
120         /*
121          *  using the same handler, we can deal with
122          *  null references as well
123          */
124         assertEvalEquals(NO_REFERENCE_VALUE, "$floobie");
125 
126         context = outer;
127     }
128 
129     private void doTestMethodExceptionEventHandler1()
130             throws Exception
131     {
132         VelocityContext outer = context;
133         context = new VelocityContext(context);
134 
135         /*
136          *  finally, we test a method exception event - we do this
137          *  by putting this class in the context, and calling
138          *  a method that does nothing but throw an exception.
139          *  we use flag in the context to turn the event handling
140          *  on and off
141          *
142          *  Note also how the reference insertion process
143          *  happens as well
144          */
145         context.put("allow_exception",Boolean.TRUE);
146         context.put("this", this );
147 
148         evaluate(" $this.throwException()");
149 
150         context = outer;
151     }
152 
153     private void doTestMethodExceptionEventHandler2()
154             throws Exception
155     {
156         VelocityContext outer = context;
157         context = new VelocityContext(context);
158         context.put("this", this );
159 
160         /*
161          *  now, we remove the exception flag, and we can see that the
162          *  exception will propgate all the way up here, and
163          *  wil be caught by the catch() block below
164          */
165         assertEvalException("$this.throwException()", MethodInvocationException.class);
166 
167         context = outer;
168     }
169 
170     /**
171      *  silly method to throw an exception to test
172      *  the method invocation exception event handling
173      */
174     public void throwException()
175             throws Exception
176     {
177         throw new Exception("Hello from throwException()");
178     }
179 
180 
181 
182     public static class TestEventCartridge
183             implements ReferenceInsertionEventHandler,
184                        MethodExceptionEventHandler,
185                        RuntimeServicesAware,ContextAware
186     {
187         private RuntimeServices rs;
188 
189         /**
190          * Required by EventHandler
191          */
192         public void setRuntimeServices( RuntimeServices rs )
193         {
194             // make sure this is only called once
195             if (this.rs == null)
196                 this.rs = rs;
197 
198             else
199                 fail("initialize called more than once.");
200         }
201 
202         /**
203          *  Event handler for when a reference is inserted into the output stream.
204          */
205         public Object referenceInsert( String reference, Object value  )
206         {
207             // as a test, make sure this EventHandler is initialized
208             if (rs == null)
209                 fail ("Event handler not initialized!");
210 
211 
212             /*
213              *  if we have a value
214              *  return a known value
215              */
216             String s = null;
217 
218             if( value != null )
219             {
220                 s = REFERENCE_VALUE;
221             }
222             else
223             {
224                 /*
225                  * we only want to deal with $floobie - anything
226                  *  else we let go
227                  */
228                 if ( reference.equals("$floobie") )
229                 {
230                     s = NO_REFERENCE_VALUE;
231                 }
232             }
233             return s;
234         }
235 
236         /**
237          *  Handles exceptions thrown during in-template method access
238          */
239         public Object methodException( Class claz, String method, Exception e )
240         {
241             // as a test, make sure this EventHandler is initialized
242             if (rs == null)
243                 fail ("Event handler not initialized!");
244 
245             // only do processing if the switch is on
246             if (context != null)
247             {
248                 boolean exceptionSwitch = context.containsKey("allow_exception");
249 
250                 if( exceptionSwitch && method.equals("throwException"))
251                 {
252                     return "handler";
253                 }
254                 else
255                     throw new RuntimeException(e);
256 
257             } else
258 
259                 throw new RuntimeException(e);
260         }
261 
262         Context context;
263 
264 
265         public void setContext(Context context)
266         {
267             this.context = context;
268         }
269     }
270 }