001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.mock2;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.io.*;
018import java.security.*;
019import java.util.*;
020
021import javax.servlet.*;
022import javax.servlet.http.*;
023
024import org.apache.http.*;
025import org.apache.juneau.collections.*;
026import org.apache.juneau.internal.*;
027import org.apache.juneau.rest.util.*;
028import org.apache.juneau.rest.util.RestUtils;
029import org.apache.juneau.urlencoding.*;
030
031/**
032 * A mutable implementation of {@link HttpServletRequest} for mocking purposes.
033 *
034 * <ul class='seealso'>
035 *    <li class='link'>{@doc juneau-rest-mock}
036 * </ul>
037 */
038public class MockServletRequest implements HttpServletRequest {
039
040   private String method = "GET";
041   private Map<String,String[]> queryDataMap = new LinkedHashMap<>();
042   private Map<String,String[]> formDataMap;
043   private Map<String,String[]> headerMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
044   private Map<String,Object> attributeMap = new LinkedHashMap<>();
045   private String characterEncoding = "UTF-8";
046   private byte[] body = new byte[0];
047   private String protocol = "HTTP/1.1";
048   private String scheme = "http";
049   private String serverName = "localhost";
050   private int serverPort = 8080;
051   private String remoteAddr = "";
052   private String remoteHost = "";
053   private Locale locale = Locale.ENGLISH;
054   private int remotePort;
055   private String localName;
056   private String localAddr;
057   private int localPort;
058   private Map<String,RequestDispatcher> requestDispatcher = new LinkedHashMap<>();
059   private ServletContext servletContext;
060   private DispatcherType dispatcherType;
061   private String authType;
062   private Cookie[] cookies;
063   private String pathInfo;
064   private String pathTranslated;
065   private String contextPath = "";
066   private String queryString;
067   private String remoteUser;
068   private Principal userPrincipal;
069   private String requestedSessionId;
070   private String requestURI;
071   private String servletPath = "";
072   private HttpSession httpSession = MockHttpSession.create();
073   private String uri = "";
074   private Set<String> roles = new LinkedHashSet<>();
075
076   /**
077    * Creates a new servlet request.
078    *
079    * Initialized with the following:
080    * <ul>
081    *    <li><c>"Accept: text/json+simple"</c>
082    *    <li><c>"Content-Type: text/json"</c>
083    * </ul>
084    *
085    * @return A new request.
086    */
087   public static MockServletRequest create() {
088      MockServletRequest r = new MockServletRequest();
089      return r;
090   }
091
092   /**
093    * Creates a new servlet request with the specified method name and request path.
094    *
095    * Initialized with the following:
096    * <ul>
097    *    <li><c>"Accept: text/json+simple"</c>
098    *    <li><c>"Content-Type: text/json"</c>
099    * </ul>
100    *
101    * @param method The HTTP method  name.
102    * @param uri The request path.
103    * @param pathArgs Optional path arguments.
104    *
105    * @return A new request.
106    */
107   public static MockServletRequest create(String method, String uri, Object...pathArgs) {
108      return create()
109         .method(method)
110         .uri(StringUtils.format(uri, pathArgs));
111   }
112
113   /**
114    * Fluent setter.
115    *
116    * @param uri The URI of the request.
117    * @return This object (for method chaining).
118    */
119   public MockServletRequest uri(String uri) {
120      uri = emptyIfNull(uri);
121      this.uri = uri;
122
123      if (uri.indexOf('?') != -1) {
124         String qs = uri.substring(uri.indexOf('?') + 1);
125         if (qs.indexOf('#') != -1)
126            qs = qs.substring(0, qs.indexOf('#'));
127         queryString = qs;
128         queryDataMap.putAll(RestUtils.parseQuery(qs));
129      }
130
131      return this;
132   }
133
134   /**
135    * Adds the specified roles on this request.
136    *
137    * @param roles The roles to add to this request (e.g. <js>"ROLE_ADMIN"</js>).
138    * @return This object (for method chaining).
139    */
140   public MockServletRequest roles(String...roles) {
141      this.roles = ASet.of(roles);
142      return this;
143   }
144
145   /**
146    * Adds the specified parent path variables to this servlet request.
147    *
148    * <p>
149    * See {@link MockRestClientBuilder#pathVars(Map)} for an example.
150    *
151    * @param pathVars The
152    * @return This object (for method chaining).
153    * @see MockRestClientBuilder#pathVars(Map)
154    */
155   public MockServletRequest pathVars(Map<String,String> pathVars) {
156      if (isNotEmpty(pathVars))
157         this.attributeMap.put("juneau.pathVars", new TreeMap<>(pathVars));
158      return this;
159   }
160
161   /**
162    * Add resolved path variables to this client.
163    *
164    * <p>
165    * Identical to {@link #pathVars(Map)} but allows you to specify as a list of key/value pairs.
166    *
167    * @param pairs The key/value pairs.  Must be an even number of parameters.
168    * @return This object (for method chaining).
169    */
170   public MockServletRequest pathVars(String...pairs) {
171      return pathVars(AMap.<String,String>ofPairs((Object[])pairs));
172   }
173
174   /**
175    * Adds the specified role on this request.
176    *
177    * @param role The role to add to this request (e.g. <js>"ROLE_ADMIN"</js>).
178    * @return This object (for method chaining).
179    */
180   public MockServletRequest role(String role) {
181      this.roles = ASet.of(role);
182      return this;
183   }
184
185   /**
186    * Fluent setter.
187    *
188    * @param value The method name for this request.
189    * @return This object (for method chaining).
190    */
191   public MockServletRequest method(String value) {
192      this.method = value;
193      return this;
194   }
195
196   /**
197    * Fluent setter.
198    *
199    * @param value The character encoding.
200    * @return This object (for method chaining).
201    */
202   public MockServletRequest characterEncoding(String value) {
203      this.characterEncoding = value;
204      return this;
205   }
206
207   /**
208    * Fluent setter.
209    *
210    * @param value The protocol.
211    * @return This object (for method chaining).
212    */
213   public MockServletRequest protocol(String value) {
214      this.protocol = value;
215      return this;
216   }
217
218   /**
219    * Fluent setter.
220    *
221    * @param value The scheme.
222    * @return This object (for method chaining).
223    */
224   public MockServletRequest scheme(String value) {
225      this.scheme = value;
226      return this;
227   }
228
229   /**
230    * Fluent setter.
231    *
232    * @param value The server name.
233    * @return This object (for method chaining).
234    */
235   public MockServletRequest serverName(String value) {
236      this.serverName = value;
237      return this;
238   }
239
240   /**
241    * Fluent setter.
242    *
243    * @param value The server port.
244    * @return This object (for method chaining).
245    */
246   public MockServletRequest serverPort(int value) {
247      this.serverPort = value;
248      return this;
249   }
250
251   /**
252    * Fluent setter.
253    *
254    * @param value The remote address.
255    * @return This object (for method chaining).
256    */
257   public MockServletRequest remoteAddr(String value) {
258      this.remoteAddr = value;
259      return this;
260   }
261
262   /**
263    * Fluent setter.
264    *
265    * @param value The remote port.
266    * @return This object (for method chaining).
267    */
268   public MockServletRequest remoteHost(String value) {
269      this.remoteHost = value;
270      return this;
271   }
272
273   /**
274    * Fluent setter.
275    *
276    * @param value The locale.
277    * @return This object (for method chaining).
278    */
279   public MockServletRequest locale(Locale value) {
280      this.locale = value;
281      return this;
282   }
283
284   /**
285    * Fluent setter.
286    *
287    * @param value The remote port.
288    * @return This object (for method chaining).
289    */
290   public MockServletRequest remotePort(int value) {
291      this.remotePort = value;
292      return this;
293   }
294
295   /**
296    * Fluent setter.
297    *
298    * @param value The local name.
299    * @return This object (for method chaining).
300    */
301   public MockServletRequest localName(String value) {
302      this.localName = value;
303      return this;
304   }
305
306   /**
307    * Fluent setter.
308    *
309    * @param value The local address.
310    * @return This object (for method chaining).
311    */
312   public MockServletRequest localAddr(String value) {
313      this.localAddr = value;
314      return this;
315   }
316
317   /**
318    * Fluent setter.
319    *
320    * @param value The local port.
321    * @return This object (for method chaining).
322    */
323   public MockServletRequest localPort(int value) {
324      this.localPort = value;
325      return this;
326   }
327
328   /**
329    * Fluent setter.
330    *
331    * @param name The path to resolve.
332    * @param value The request dispatcher.
333    * @return This object (for method chaining).
334    */
335   public MockServletRequest requestDispatcher(String name, RequestDispatcher value) {
336      this.requestDispatcher.put(name, value);
337      return this;
338   }
339
340   /**
341    * Fluent setter.
342    *
343    * @param value The servlet context.
344    * @return This object (for method chaining).
345    */
346   public MockServletRequest servletContext(ServletContext value) {
347      this.servletContext = value;
348      return this;
349   }
350
351   /**
352    * Fluent setter.
353    *
354    * @param value The dispatcher type.
355    * @return This object (for method chaining).
356    */
357   public MockServletRequest dispatcherType(DispatcherType value) {
358      this.dispatcherType = value;
359      return this;
360   }
361
362   /**
363    * Fluent setter.
364    *
365    * @param value The auth type.
366    * @return This object (for method chaining).
367    */
368   public MockServletRequest authType(String value) {
369      this.authType = value;
370      return this;
371   }
372
373   /**
374    * Fluent setter.
375    *
376    * @param value The cookies.
377    * @return This object (for method chaining).
378    */
379   public MockServletRequest cookies(Cookie[] value) {
380      this.cookies = value;
381      return this;
382   }
383
384   /**
385    * Fluent setter.
386    *
387    * @param value The path info.
388    * @return This object (for method chaining).
389    */
390   public MockServletRequest pathInfo(String value) {
391      this.pathInfo = value;
392      return this;
393   }
394
395   /**
396    * Fluent setter.
397    *
398    * @param value The path translated.
399    * @return This object (for method chaining).
400    */
401   public MockServletRequest pathTranslated(String value) {
402      this.pathTranslated = value;
403      return this;
404   }
405
406   /**
407    * Fluent setter.
408    *
409    * @param value The context path.
410    * @return This object (for method chaining).
411    */
412   public MockServletRequest contextPath(String value) {
413      this.contextPath = value;
414      return this;
415   }
416
417   /**
418    * Fluent setter.
419    *
420    * @param value The query string.
421    * @return This object (for method chaining).
422    */
423   public MockServletRequest queryString(String value) {
424      this.queryString = value;
425      return this;
426   }
427
428   /**
429    * Fluent setter.
430    *
431    * @param value The remote user.
432    * @return This object (for method chaining).
433    */
434   public MockServletRequest remoteUser(String value) {
435      this.remoteUser = value;
436      return this;
437   }
438
439   /**
440    * Fluent setter.
441    *
442    * @param value The user principal.
443    * @return This object (for method chaining).
444    */
445   public MockServletRequest userPrincipal(Principal value) {
446      this.userPrincipal = value;
447      return this;
448   }
449
450   /**
451    * Fluent setter.
452    *
453    * @param value The requested session ID.
454    * @return This object (for method chaining).
455    */
456   public MockServletRequest requestedSessionId(String value) {
457      this.requestedSessionId = value;
458      return this;
459   }
460
461   /**
462    * Fluent setter.
463    *
464    * @param value The request URI.
465    * @return This object (for method chaining).
466    */
467   public MockServletRequest requestURI(String value) {
468      this.requestURI = value;
469      return this;
470   }
471
472   /**
473    * Fluent setter.
474    *
475    * @param value The servlet path.
476    * @return This object (for method chaining).
477    */
478   public MockServletRequest servletPath(String value) {
479      this.servletPath = value;
480      return this;
481   }
482
483   /**
484    * Fluent setter.
485    *
486    * @param value The HTTP session.
487    * @return This object (for method chaining).
488    */
489   public MockServletRequest httpSession(HttpSession value) {
490      this.httpSession = value;
491      return this;
492   }
493
494   @Override /* HttpServletRequest */
495   public Object getAttribute(String name) {
496      return attributeMap.get(name);
497   }
498
499   @Override /* HttpServletRequest */
500   public Enumeration<String> getAttributeNames() {
501      return Collections.enumeration(attributeMap.keySet());
502   }
503
504   @Override /* HttpServletRequest */
505   public String getCharacterEncoding() {
506      return characterEncoding;
507   }
508
509   @Override /* HttpServletRequest */
510   public void setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException {
511      this.characterEncoding = characterEncoding;
512   }
513
514   @Override /* HttpServletRequest */
515   public int getContentLength() {
516      return body == null ? 0 : body.length;
517   }
518
519   @Override /* HttpServletRequest */
520   public long getContentLengthLong() {
521      return body == null ? 0 : body.length;
522   }
523
524   @Override /* HttpServletRequest */
525   public String getContentType() {
526      return getHeader("Content-Type");
527   }
528
529   @Override /* HttpServletRequest */
530   public ServletInputStream getInputStream() throws IOException {
531      if (formDataMap != null)
532         body = UrlEncodingSerializer.DEFAULT.toString(formDataMap).getBytes();
533      return new BoundedServletInputStream(new ByteArrayInputStream(body), Integer.MAX_VALUE);
534   }
535
536   @Override /* HttpServletRequest */
537   public String getParameter(String name) {
538      String[] s = getParameterMap().get(name);
539      return s == null || s.length == 0 ? null : s[0];
540   }
541
542   @Override /* HttpServletRequest */
543   public Enumeration<String> getParameterNames() {
544      return Collections.enumeration(new ArrayList<>(getParameterMap().keySet()));
545   }
546
547   @Override /* HttpServletRequest */
548   public String[] getParameterValues(String name) {
549      return getParameterMap().get(name);
550   }
551
552   @Override /* HttpServletRequest */
553   public Map<String,String[]> getParameterMap() {
554      if ("POST".equalsIgnoreCase(method)) {
555         if (formDataMap == null) {
556            try {
557               formDataMap = RestUtils.parseQuery(IOUtils.read(body));
558            } catch (IOException e) {
559               e.printStackTrace();
560            }
561         }
562         return formDataMap;
563      }
564      return queryDataMap;
565   }
566
567   @Override /* HttpServletRequest */
568   public String getProtocol() {
569      return protocol;
570   }
571
572   @Override /* HttpServletRequest */
573   public String getScheme() {
574      return scheme;
575   }
576
577   @Override /* HttpServletRequest */
578   public String getServerName() {
579      return serverName;
580   }
581
582   @Override /* HttpServletRequest */
583   public int getServerPort() {
584      return serverPort;
585   }
586
587   @Override /* HttpServletRequest */
588   public BufferedReader getReader() throws IOException {
589      return new BufferedReader(new InputStreamReader(getInputStream(), characterEncoding));
590   }
591
592   @Override /* HttpServletRequest */
593   public String getRemoteAddr() {
594      return remoteAddr;
595   }
596
597   @Override /* HttpServletRequest */
598   public String getRemoteHost() {
599      return remoteHost;
600   }
601
602   @Override /* HttpServletRequest */
603   public void setAttribute(String name, Object o) {
604      this.attributeMap.put(name, o);
605   }
606
607   @Override /* HttpServletRequest */
608   public void removeAttribute(String name) {
609      this.attributeMap.remove(name);
610   }
611
612   @Override /* HttpServletRequest */
613   public Locale getLocale() {
614      return locale;
615   }
616
617   @Override /* HttpServletRequest */
618   public Enumeration<Locale> getLocales() {
619      return Collections.enumeration(Arrays.asList(locale));
620   }
621
622   @Override /* HttpServletRequest */
623   public boolean isSecure() {
624      return false;
625   }
626
627   @Override /* HttpServletRequest */
628   public RequestDispatcher getRequestDispatcher(String path) {
629      return requestDispatcher.get(path);
630   }
631
632   @Override /* HttpServletRequest */
633   public String getRealPath(String path) {
634      return path;
635   }
636
637   @Override /* HttpServletRequest */
638   public int getRemotePort() {
639      return remotePort;
640   }
641
642   @Override /* HttpServletRequest */
643   public String getLocalName() {
644      return localName;
645   }
646
647   @Override /* HttpServletRequest */
648   public String getLocalAddr() {
649      return localAddr;
650   }
651
652   @Override /* HttpServletRequest */
653   public int getLocalPort() {
654      return localPort;
655   }
656
657   @Override /* HttpServletRequest */
658   public ServletContext getServletContext() {
659      return servletContext;
660   }
661
662   @Override /* HttpServletRequest */
663   public AsyncContext startAsync() throws IllegalStateException {
664      return null;
665   }
666
667   @Override /* HttpServletRequest */
668   public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
669      return null;
670   }
671
672   @Override /* HttpServletRequest */
673   public boolean isAsyncStarted() {
674      return false;
675   }
676
677   @Override /* HttpServletRequest */
678   public boolean isAsyncSupported() {
679      return false;
680   }
681
682   @Override /* HttpServletRequest */
683   public AsyncContext getAsyncContext() {
684      return null;
685   }
686
687   @Override /* HttpServletRequest */
688   public DispatcherType getDispatcherType() {
689      return dispatcherType;
690   }
691
692   @Override /* HttpServletRequest */
693   public String getAuthType() {
694      return authType;
695   }
696
697   @Override /* HttpServletRequest */
698   public Cookie[] getCookies() {
699      return cookies;
700   }
701
702   @Override /* HttpServletRequest */
703   public long getDateHeader(String name) {
704      String s = getHeader(name);
705      return s == null ? 0 : org.apache.juneau.http.header.Date.of(s).asZonedDateTime().toInstant().toEpochMilli();
706   }
707
708   @Override /* HttpServletRequest */
709   public String getHeader(String name) {
710      String[] s = headerMap.get(name);
711      return s == null || s.length == 0 ? null : s[0];
712   }
713
714   @Override /* HttpServletRequest */
715   public Enumeration<String> getHeaders(String name) {
716      String[] s = headerMap.get(name);
717      return Collections.enumeration(Arrays.asList(s == null ? new String[0] : s));
718   }
719
720   @Override /* HttpServletRequest */
721   public Enumeration<String> getHeaderNames() {
722      return Collections.enumeration(headerMap.keySet());
723   }
724
725   @Override /* HttpServletRequest */
726   public int getIntHeader(String name) {
727      String s = getHeader(name);
728      return s == null || s.isEmpty() ? 0 : Integer.parseInt(s);
729   }
730
731   @Override /* HttpServletRequest */
732   public String getMethod() {
733      return method;
734   }
735
736   @Override /* HttpServletRequest */
737   public String getPathInfo() {
738      if (pathInfo == null) {
739         pathInfo = getRequestURI();
740         if (isNotEmpty(contextPath))
741            pathInfo = pathInfo.substring(contextPath.length());
742         if (isNotEmpty(servletPath))
743            pathInfo = pathInfo.substring(servletPath.length());
744      }
745      return nullIfEmpty(urlDecode(pathInfo));
746   }
747
748   @Override /* HttpServletRequest */
749   public String getPathTranslated() {
750      if (pathTranslated == null)
751         pathTranslated = "/mock-path" + getPathInfo();
752      return pathTranslated;
753   }
754
755   @Override /* HttpServletRequest */
756   public String getContextPath() {
757      return contextPath;
758   }
759
760   @Override /* HttpServletRequest */
761   public String getQueryString() {
762      if (queryString == null) {
763         if (queryDataMap.isEmpty())
764            queryString = "";
765         else {
766            StringBuilder sb = new StringBuilder();
767            for (Map.Entry<String,String[]> e : queryDataMap.entrySet())
768               if (e.getValue() == null)
769                  sb.append(sb.length() == 0 ? "" : "&").append(urlEncode(e.getKey()));
770               else for (String v : e.getValue())
771                  sb.append(sb.length() == 0 ? "" : "&").append(urlEncode(e.getKey())).append('=').append(urlEncode(v));
772            queryString = sb.toString();
773         }
774      }
775      return isEmpty(queryString) ? null : queryString;
776   }
777
778   @Override /* HttpServletRequest */
779   public String getRemoteUser() {
780      return remoteUser;
781   }
782
783   @Override /* HttpServletRequest */
784   public boolean isUserInRole(String role) {
785      return roles.contains(role);
786   }
787
788   @Override /* HttpServletRequest */
789   public Principal getUserPrincipal() {
790      return userPrincipal;
791   }
792
793   @Override /* HttpServletRequest */
794   public String getRequestedSessionId() {
795      return requestedSessionId;
796   }
797
798   @Override /* HttpServletRequest */
799   public String getRequestURI() {
800      if (requestURI == null) {
801         requestURI = uri;
802         requestURI = requestURI.replaceAll("^\\w+\\:\\/\\/[^\\/]+", "").replaceAll("\\?.*$", "");
803      }
804      return requestURI;
805   }
806
807   @Override /* HttpServletRequest */
808   public StringBuffer getRequestURL() {
809      return new StringBuffer(uri.replaceAll("\\?.*$", ""));
810   }
811
812   @Override /* HttpServletRequest */
813   public String getServletPath() {
814      return servletPath;
815   }
816
817   @Override /* HttpServletRequest */
818   public HttpSession getSession(boolean create) {
819      return httpSession;
820   }
821
822   @Override /* HttpServletRequest */
823   public HttpSession getSession() {
824      return httpSession;
825   }
826
827   @Override /* HttpServletRequest */
828   public String changeSessionId() {
829      return null;
830   }
831
832   @Override /* HttpServletRequest */
833   public boolean isRequestedSessionIdValid() {
834      return false;
835   }
836
837   @Override /* HttpServletRequest */
838   public boolean isRequestedSessionIdFromCookie() {
839      return false;
840   }
841
842   @Override /* HttpServletRequest */
843   public boolean isRequestedSessionIdFromURL() {
844      return false;
845   }
846
847   @Override /* HttpServletRequest */
848   public boolean isRequestedSessionIdFromUrl() {
849      return false;
850   }
851
852   @Override /* HttpServletRequest */
853   public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
854      return false;
855   }
856
857   @Override /* HttpServletRequest */
858   public void login(String username, String password) throws ServletException {
859   }
860
861   @Override /* HttpServletRequest */
862   public void logout() throws ServletException {
863   }
864
865   @Override /* HttpServletRequest */
866   public Collection<Part> getParts() throws IOException, ServletException {
867      return null;
868   }
869
870   @Override /* HttpServletRequest */
871   public Part getPart(String name) throws IOException, ServletException {
872      return null;
873   }
874
875   @Override /* HttpServletRequest */
876   public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
877      return null;
878   }
879
880   //=================================================================================================================
881   // Convenience methods
882   //=================================================================================================================
883
884   /**
885    * Fluent setter.
886    *
887    * @param name Header name.
888    * @param value
889    *    Header value.
890    *    <br>The value is converted to a simple string using {@link Object#toString()}.
891    * @return This object (for method chaining).
892    */
893   public MockServletRequest header(String name, Object value) {
894      if (value != null) {
895         String[] v1 = (value instanceof String[]) ? (String[])value : new String[]{value.toString()};
896         String[] v2 = headerMap.get(name);
897         String[] v3 = ArrayUtils.combine(v2, v1);
898         headerMap.put(name, v3);
899      }
900      return this;
901   }
902
903   /**
904    * Fluent setter.
905    *
906    * @param name Request attribute name.
907    * @param value Request attribute value.
908    * @return This object (for method chaining).
909    */
910   public MockServletRequest attribute(String name, Object value) {
911      this.attributeMap.put(name, value);
912      return this;
913   }
914
915   /**
916    * Fluent setter.
917    *
918    * @param value
919    *    The body of the request.
920    *    <br>Can be any of the following data types:
921    *    <ul>
922    *       <li><code><jk>byte</jk>[]</code>
923    *       <li>{@link Reader}
924    *       <li>{@link InputStream}
925    *       <li>{@link CharSequence}
926    *    </ul>
927    *    Any other types are converted to a string using the <c>toString()</c> method.
928    * @return This object (for method chaining).
929    */
930   public MockServletRequest body(Object value) {
931      try {
932         if (value instanceof byte[])
933            this.body = (byte[])value;
934         else if (value instanceof Reader)
935            this.body = IOUtils.read((Reader)value).getBytes();
936         else if (value instanceof InputStream)
937            this.body = IOUtils.readBytes((InputStream)value, 1024);
938         else if (value instanceof CharSequence)
939            this.body = ((CharSequence)value).toString().getBytes();
940         else if (value != null)
941            this.body = value.toString().getBytes();
942      } catch (IOException e) {
943         throw new RuntimeException(e);
944      }
945      return this;
946   }
947
948   //=================================================================================================================
949   // Convenience methods - headers
950   //=================================================================================================================
951
952   /**
953    * Enabled debug mode on this request.
954    *
955    * <p>
956    * Causes information about the request execution to be sent to STDERR.
957    *
958    * @param value The enable flag value.
959    * @return This object (for method chaining).
960    */
961   protected MockServletRequest debug(boolean value) {
962      if (value)
963         header("X-Debug", true);
964      return this;
965   }
966
967   /**
968    * Enabled debug mode on this request.
969    *
970    * <p>
971    * Prevents errors from being logged on the server side if no-trace per-request is enabled.
972    *
973    * @param value The enable flag value.
974    * @return This object (for method chaining).
975    */
976   public MockServletRequest noTrace(boolean value) {
977      if (value)
978         header("X-NoTrace", true);
979      return this;
980   }
981
982   /**
983    * If the specified request is a {@link MockRestRequest}, applies any of the override values to this servlet request.
984    *
985    * @param req The request to copy overrides from.
986    * @return This object (for method chaining).
987    */
988   public MockServletRequest applyOverrides(HttpRequest req) {
989
990      if (req instanceof MockRestRequest) {
991         MockRestRequest mreq = (MockRestRequest)req;
992         for (Map.Entry<String,Object> a : mreq.getAttributeMap().entrySet())
993            attribute(a.getKey(), a.getValue());
994         for (Map.Entry<String,RequestDispatcher> a : mreq.getRequestDispatcherMap().entrySet())
995            requestDispatcher(a.getKey(), a.getValue());
996         if (mreq.getCharacterEncoding() != null)
997            characterEncoding(mreq.getCharacterEncoding());
998         if (mreq.getProtocol() != null)
999            protocol(mreq.getProtocol());
1000         if (mreq.getScheme() != null)
1001            scheme(mreq.getScheme());
1002         if (mreq.getServerName() != null)
1003            serverName(mreq.getServerName());
1004         if (mreq.getRemoteAddr() != null)
1005            remoteAddr(mreq.getRemoteAddr());
1006         if (mreq.getRemoteHost() != null)
1007            remoteHost(mreq.getRemoteHost());
1008         if (mreq.getLocalName() != null)
1009            localName(mreq.getLocalName());
1010         if (mreq.getLocalAddr() != null)
1011            localAddr(mreq.getLocalAddr());
1012         if (mreq.getPathInfo() != null)
1013            pathInfo(mreq.getPathInfo());
1014         if (mreq.getPathTranslated() != null)
1015            pathTranslated(mreq.getPathTranslated());
1016         if (mreq.getContextPath() != null)
1017            contextPath(mreq.getContextPath());
1018         if (mreq.getQueryString() != null)
1019            queryString(mreq.getQueryString());
1020         if (mreq.getRemoteUser() != null)
1021            remoteUser(mreq.getRemoteUser());
1022         if (mreq.getRequestedSessionId() != null)
1023            requestedSessionId(mreq.getRequestedSessionId());
1024         if (mreq.getRequestURI() != null)
1025            requestURI(mreq.getRequestURI());
1026         if (mreq.getServletPath() != null)
1027            servletPath(mreq.getServletPath());
1028         if (mreq.getAuthType() != null)
1029            authType(mreq.getAuthType());
1030         if (mreq.getServerPort() != null)
1031            serverPort(mreq.getServerPort());
1032         if (mreq.getRemotePort() != null)
1033            remotePort(mreq.getRemotePort());
1034         if (mreq.getLocalPort() != null)
1035            localPort(mreq.getLocalPort());
1036         if (mreq.getLocale() != null)
1037            locale(mreq.getLocale());
1038         if (mreq.getServletContext() != null)
1039            servletContext(mreq.getServletContext());
1040         if (mreq.getDispatcherType() != null)
1041            dispatcherType(mreq.getDispatcherType());
1042         if (mreq.getCookies() != null)
1043            cookies(mreq.getCookies());
1044         if (mreq.getUserPrincipal() != null)
1045            userPrincipal(mreq.getUserPrincipal());
1046         if (mreq.getHttpSession() != null)
1047            httpSession(mreq.getHttpSession());
1048         if (mreq.getRoles() != null)
1049            roles(mreq.getRoles());
1050      }
1051
1052      return this;
1053   }
1054}