1   package servletunit;
2   
3   //  StrutsTestCase - a JUnit extension for testing Struts actions
4   //  within the context of the ActionServlet.
5   //  Copyright (C) 2002 Deryl Seale
6   //
7   //  This library is free software; you can redistribute it and/or
8   //  modify it under the terms of the Apache Software License as
9   //  published by the Apache Software Foundation; either version 1.1
10  //  of the License, or (at your option) any later version.
11  //
12  //  This library is distributed in the hope that it will be useful,
13  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  //  Apache Software Foundation Licens for more details.
16  //
17  //  You may view the full text here: http://www.apache.org/LICENSE.txt
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  
22  import javax.servlet.RequestDispatcher;
23  import javax.servlet.Servlet;
24  import javax.servlet.ServletContext;
25  import javax.servlet.ServletException;
26  import java.io.InputStream;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.net.MalformedURLException;
30  import java.net.URL;
31  import java.util.Enumeration;
32  import java.util.Hashtable;
33  import java.util.Set;
34  
35  /**
36   * This class simulates a ServletContext.
37   */
38  public class ServletContextSimulator implements ServletContext
39  {
40  
41      private Hashtable initParameters;
42      private Hashtable attributes;
43      private RequestDispatcherSimulator dispatcher = null;
44      private static Log logger = LogFactory.getLog( ServletContextSimulator.class );
45      private File contextDirectory;
46  
47      public ServletContextSimulator() {
48          this.initParameters = new Hashtable();
49          this.attributes = new Hashtable();
50      }
51  
52      /**
53       * Returns the servlet container attribute with the given name,
54       * or <code>null</code> if there is no attribute by that name.
55       * An attribute allows a servlet container to give the
56       * servlet additional information not
57       * already provided by this interface. See your
58       * server documentation for information about its attributes.
59       * A list of supported attributes can be retrieved using
60       * <code>getAttributeNames</code>.
61       *
62       * <p>The attribute is returned as a <code>java.lang.Object</code>
63       * or some subclass.
64       * Attribute names should follow the same convention as package
65       * names. The Java Servlet API specification reserves names
66       * matching <code>java.*</code>, <code>javax.*</code>,
67       * and <code>sun.*</code>.
68       *
69       *
70       * @param name      a <code>String</code> specifying the name
71       *                  of the attribute
72       *
73       * @return          an <code>Object</code> containing the value
74       *                  of the attribute, or <code>null</code>
75       *                  if no attribute exists matching the given
76       *                  name
77       *
78       * @see             ServletContext#getAttributeNames
79       *
80       */
81      public Object getAttribute(String name)
82      {
83          return attributes.get(name);
84      }
85  
86      /**
87       * Returns an <code>Enumeration</code> containing the
88       * attribute names available
89       * within this servlet context. Use the
90       * {@link #getAttribute} method with an attribute name
91       * to get the value of an attribute.
92       *
93       * @return          an <code>Enumeration</code> of attribute
94       *                  names
95       *
96       * @see             #getAttribute
97       *
98       */
99      public Enumeration getAttributeNames()
100     {
101         return attributes.keys();
102     }
103 
104     /**
105      * Unsupported in this version.
106      */
107     public ServletContext getContext(String uripath)
108     {
109         throw new UnsupportedOperationException("getContext operation is not supported!");
110     }
111 
112     /**
113      * Returns a <code>String</code> containing the value of the named
114      * context-wide initialization parameter, or <code>null</code> if the
115      * parameter does not exist.
116      *
117      * <p>This method can make available configuration information useful
118      * to an entire "web application".  For example, it can provide a
119      * webmaster's email address or the name of a system that holds
120      * critical data.
121      *
122      * @param   s    a <code>String</code> containing the name of the
123      *                  parameter whose value is requested
124      *
125      * @return          a <code>String</code> containing at least the
126      *                  servlet container name and version number
127      *
128      * @see javax.servlet.ServletConfig#getInitParameter
129      */
130     public String getInitParameter(String s)
131     {
132         return (String) initParameters.get(s);
133     }
134 
135     /**
136      * Returns the names of the context's initialization parameters as an
137      * <code>Enumeration</code> of <code>String</code> objects, or an
138      * empty <code>Enumeration</code> if the context has no initialization
139      * parameters.
140      *
141      * @return          an <code>Enumeration</code> of <code>String</code>
142      *                  objects containing the names of the context's
143      *                  initialization parameters
144      *
145      * @see javax.servlet.ServletConfig#getInitParameter
146      */
147     public Enumeration getInitParameterNames()
148     {
149         return initParameters.keys();
150     }
151 
152     /**
153      * Sets a named initialization parameter with the supplied
154      * <code>String</code> value.
155      *
156      * @param key      a <code>String</code> specifying the name
157      *                  of the initialization parameter
158      *
159      * @param value     a <code>String</code> value for this initialization
160      *                  parameter
161      *
162      */
163     public void setInitParameter(String key,String value)
164     {
165         initParameters.put(key,value);
166     }
167 
168     /**
169      * Returns the major version of the Java Servlet API that this
170      * Web server supports. All implementations that comply
171      * with Version 2.3 must have this method
172      * return the integer 2.
173      *
174      * @return              2
175      *
176      */
177     public int getMajorVersion()
178     {
179         return 2;
180     }
181 
182     /**
183      * Unsupported in this version.
184      */
185     public String getMimeType(String file)
186     {
187         throw new UnsupportedOperationException("getMimeType operation is not supported!");
188     }
189 
190     /**
191      * Returns the minor version of the Servlet API that this
192      * Web server supports. All implementations that comply
193      * with Version 2.3 must have this method
194      * return the integer 1.
195      *
196      * @return              3
197      *
198      */
199     public int getMinorVersion()
200     {
201         return 3;
202     }
203 
204     public RequestDispatcher getNamedDispatcher(String s)
205     {
206         throw new UnsupportedOperationException("getNamedDispatcher operation is not supported!");
207     }
208 
209     public String getRealPath(String path)
210     {
211         if ((contextDirectory == null) || (path == null))
212             return null;
213         else
214             return (new File(contextDirectory, path)).getAbsolutePath();
215     }
216 
217     /**
218      *
219      * Returns a {@link RequestDispatcher} object that acts
220      * as a wrapper for the resource located at the given path.
221      * A <code>RequestDispatcher</code> object can be used to forward
222      * a request to the resource or to include the resource in a response.
223      * The resource can be dynamic or static.
224      *
225      * <p>The pathname must begin with a "/" and is interpreted as relative
226      * to the current context root.  Use <code>getContext</code> to obtain
227      * a <code>RequestDispatcher</code> for resources in foreign contexts.
228      * This method returns <code>null</code> if the <code>ServletContext</code>
229      * cannot return a <code>RequestDispatcher</code>.
230      *
231      * @param urlpath      a <code>String</code> specifying the pathname
232      *                  to the resource
233      *
234      * @return          a <code>RequestDispatcher</code> object
235      *                  that acts as a wrapper for the resource
236      *                  at the specified path
237      *
238      * @see             RequestDispatcher
239      * @see             ServletContext#getContext
240      *
241      */
242     public RequestDispatcher getRequestDispatcher(String urlpath)
243     {
244         dispatcher =  new RequestDispatcherSimulator(urlpath);
245         return dispatcher;
246     }
247 
248     /**
249      * Returns the mock RequestDispatcher object used in this test.
250      * The RequestDispatcherSimulator contains forwarding information
251      * that can be used in test validation.
252      */
253     public RequestDispatcherSimulator getRequestDispatcherSimulator() {
254         return dispatcher;
255     }
256 
257     /**
258      * TODO: add appropriate comments
259      */
260     public URL getResource(String path) throws MalformedURLException
261     {
262         try {
263             File file = getResourceAsFile(path);
264 
265             if (file.exists()) {
266                 return file.toURL();
267             }
268             else {
269                 if(!path.startsWith("/")){
270                     path = "/" + path;
271                 }
272                 return this.getClass().getResource(path);
273             }
274         } catch (Exception e) {
275             return null;
276         }
277     }
278 
279     /**
280      * Returns the resource located at the named path as
281      * an <code>InputStream</code> object.
282      *
283      * <p>The data in the <code>InputStream</code> can be
284      * of any type or length. The path must be specified according
285      * to the rules given in <code>getResource</code>.
286      * This method returns <code>null</code> if no resource exists at
287      * the specified path.
288      *
289      * <p>Meta-information such as content length and content type
290      * that is available via <code>getResource</code>
291      * method is lost when using this method.
292      *
293      * <p>The servlet container must implement the URL handlers
294      * and <code>URLConnection</code> objects necessary to access
295      * the resource.
296      *
297      * <p>In this mock implementation, this method first looks for
298      * the supplied pathname in the underlying filesystem; if it
299      * does not exist there, the default Java classloader is used.
300      *
301      *
302      * @param path     a <code>String</code> specifying the path
303      *                  to the resource
304      *
305      * @return          the <code>InputStream</code> returned to the
306      *                  servlet, or <code>null</code> if no resource
307      *                  exists at the specified path
308      *
309      *
310      */
311     public InputStream getResourceAsStream(String path)
312     {
313         try {
314             File file = getResourceAsFile(path);
315 
316             if (file.exists()) {
317                 return new FileInputStream(file);
318             }
319             else {
320                 if(!path.startsWith("/")){
321                     path = "/" + path;
322                 }
323                 return this.getClass().getResourceAsStream(path);
324             }
325         } catch (Exception e) {
326             System.out.println("caught error: " + e);
327             e.printStackTrace();
328             return null;
329         }
330     }
331 
332     /**
333      * Attempts to load a resource from the underlying file system
334      * and return a file handle to it.
335      * It first treats the path as an absolute path.  If no file is found,
336      * it attempts to treat the path as relative to the context directory.
337      * If no file is found, it attempts to treat the path as relative to
338      * the current directory.
339      * If all these options fail, the returned file will return false()
340      * to calls to File.exists().
341      * @param path the relative or context-relative path to the file
342      * @return the refernce to the file (which may or may not exist)
343      */
344     public File getResourceAsFile(String path){
345         File file = new File(path);
346 
347         // If the path is relative then apply the contextDirectory path if it exists.
348         if (!file.exists())
349         {
350             if(!path.startsWith("/")){
351                 path = "/" + path;
352             }
353             if((getContextDirectory() != null))
354             {
355                 file = new File(getContextDirectory().getAbsolutePath() + path);
356             }else{
357                 //try using current directory
358                 file = new File(new File(".").getAbsolutePath() + path);
359             }
360         }
361         return file;
362 
363     }
364 
365     /**
366      * Unsupported in this version.
367      */
368     public Set getResourcePaths()
369     {
370         throw new UnsupportedOperationException("getResourcePaths operation is not supported!");
371     }
372 
373     /**
374      * Returns the name and version of the servlet container on which
375      * the servlet is running.
376      *
377      * <p>The form of the returned string is
378      * <i>servername</i>/<i>versionnumber</i>.
379      * For example, the JavaServer Web Development Kit may return the string
380      * <code>JavaServer Web Dev Kit/1.0</code>.
381      *
382      * <p>The servlet container may return other optional information
383      * after the primary string in parentheses, for example,
384      * <code>JavaServer Web Dev Kit/1.0 (JDK 1.1.6; Windows NT 4.0 x86)</code>.
385      *
386      *
387      * @return          a <code>String</code> containing at least the
388      *                  servlet container name and version number
389      *
390      */
391     public String getServerInfo()
392     {
393         return "MockServletEngine/1.9.5";
394     }
395 
396     /**
397      * Unsupported in this version.
398      */
399     public Servlet getServlet(String name) throws ServletException
400     {
401         throw new UnsupportedOperationException("getServlet operation is not supported!");
402     }
403 
404     /**
405      * Unsupported in this version.
406      */
407     public String getServletContextName()
408     {
409         throw new UnsupportedOperationException("getServletContextName operation is not supported!");
410     }
411 
412     /**
413      * Unsupported in this version.
414      */
415     public Enumeration getServletNames()
416     {
417         throw new UnsupportedOperationException("getServletNames operation is not supported!");
418     }
419 
420     /**
421      * Unsupported in this version.
422      */
423     public Enumeration getServlets()
424     {
425         throw new UnsupportedOperationException("getServlets operation is not supported!");
426     }
427 
428     /**
429      * @deprecated  As of Java Servlet API 2.1, use
430      *                      @link ServletContext.log(String message, Throwable throwable)
431      *                      instead.
432      *
433      * <p>This method was originally defined to write an
434      * exception's stack trace and an explanatory error message
435      * to the servlet log file.
436      *
437      */
438     public void log(Exception exception, String msg)
439     {
440         logger.info(msg + "\n" + exception.getClass() + " - " + exception.getMessage());
441     }
442 
443     /**
444      *
445      * Writes the specified message to a servlet log file, which is usually
446      * an event log. The message provides explanatory information about
447      * an exception or error or an action the servlet engine takes. The name
448      * and type of the servlet log file is specific to the servlet engine.
449      *
450      *
451      * @param msg   a <code>String</code> specifying the explanatory
452      *                      message to be written to the log file
453      *
454      */
455     public void log(String msg)
456     {
457         logger.info(msg);
458     }
459 
460     /**
461      * Writes the stack trace and an explanatory message
462      * for a given <code>Throwable</code> exception
463      * to the servlet log file.  The name and type of the servlet log
464      * file is specific to the servlet engine, but it is usually an event log.
465      *
466      *
467      * @param message               a <code>String</code> that
468      *                              describes the error or exception
469      *
470      * @param throwable     the <code>Throwable</code> error
471      *                              or exception
472      *
473      */
474     public void log(String message, Throwable throwable)
475     {
476         logger.info(message + "\n" + throwable.getClass() + " - " + throwable.getMessage());
477     }
478 
479     /**
480      * Removes the attribute with the given name from
481      * the servlet context. After removal, subsequent calls to
482      * {@link #getAttribute} to retrieve the attribute's value
483      * will return <code>null</code>.
484 
485      * <p>If listeners are configured on the <code>ServletContext</code> the
486      * container notifies them accordingly.
487 
488      *
489      *
490      * @param name      a <code>String</code> specifying the name
491      *                  of the attribute to be removed
492      *
493      */
494     public void removeAttribute(String name)
495     {
496         attributes.remove(name);
497     }
498 
499     /**
500      *
501      * Binds an object to a given attribute name in this servlet context. If
502      * the name specified is already used for an attribute, this
503      * method will replace the attribute with the new to the new attribute.
504      * <p>If listeners are configured on the <code>ServletContext</code> the
505      * container notifies them accordingly.
506      * <p>
507      * If a null value is passed, the effect is the same as calling
508      * <code>removeAttribute()</code>.
509      *
510      * <p>Attribute names should follow the same convention as package
511      * names. The Java Servlet API specification reserves names
512      * matching <code>java.*</code>, <code>javax.*</code>, and
513      * <code>sun.*</code>.
514      *
515      *
516      * @param name      a <code>String</code> specifying the name
517      *                  of the attribute
518      *
519      * @param object    an <code>Object</code> representing the
520      *                  attribute to be bound
521      *
522      *
523      *
524      */
525     public void setAttribute(String name, Object object)
526     {
527         attributes.put(name,object);
528     }
529 
530     /**
531      * Unsupported in this version.
532      */
533     public Set getResourcePaths(String path) {
534         throw new UnsupportedOperationException("getResourcePaths operation is not supported!");
535     }
536 
537     /**
538      * Sets the absolute context directory to be used in the getRealPath() method.
539      * @param contextDirectory the absolute path of the root context directory for this application.
540      */
541     public void setContextDirectory(File contextDirectory) {
542         this.contextDirectory = contextDirectory;
543     }
544 
545     public File getContextDirectory() {
546         return contextDirectory;
547     }
548 
549 
550 }