Author: buildbot
Date: Sun Jan 23 19:11:49 2022
New Revision: 1078161

Log:
Production update by buildbot for tapestry

Added:
    websites/production/tapestry/content/rest-support-580.html
Modified:
    websites/production/tapestry/content/cache/main.pageCache

Modified: websites/production/tapestry/content/cache/main.pageCache
==============================================================================
Binary files - no diff available.

Added: websites/production/tapestry/content/rest-support-580.html
==============================================================================
--- websites/production/tapestry/content/rest-support-580.html (added)
+++ websites/production/tapestry/content/rest-support-580.html Sun Jan 23 
19:11:49 2022
@@ -0,0 +1,364 @@
+<!DOCTYPE html>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta name="keywords" content="tapestry, apache, apache tapestry, framework, 
java, web, component, open source, application, dynamic, scalable, robust, 
servlet">
+  <meta name="description" content="Apache Tapestry is a open-source 
component-oriented framework for creating dynamic, robust, highly scalable web 
applications in Java. Tapestry complements and builds upon the standard Java 
Servlet API, and so it works in any servlet container or application server.">
+
+  <title>
+          REST Support (5.8.0+) - Apache Tapestry
+      </title>
+
+  <link rel="apple-touch-icon-precomposed" sizes="144x144" 
href="/images/apache-tapestry-icon-144.png">
+  <link rel="apple-touch-icon-precomposed" sizes="114x114" 
href="/images/apache-tapestry-icon-114.png">
+  <link rel="apple-touch-icon-precomposed" sizes="72x72" 
href="/images/apache-tapestry-icon-72.png">
+  <link rel="apple-touch-icon-precomposed" 
href="/images/apache-tapestry-icon-57.png">
+  <link rel="shortcut icon" href="/images/apache-tapestry-icon-32.png">
+
+  <link rel="stylesheet" 
href="https://fonts.googleapis.com/css2?family=Sarabun:ital,wght@0,400;0,700;1,400;1,700&display=swap";>
 
+  <link rel="stylesheet" 
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"; 
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
 crossorigin="anonymous">
+  <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css";>
+  <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/themes/prism.min.css";>
+  <link rel="stylesheet" href="/styles/main.css">
+
+  <script type="text/javascript">
+    if (window.location.protocol === 'http:' && window.location.hostname !== 
'localhost') {
+      window.location = window.location.href.replace('http://', 'https://');
+    }
+  </script>
+
+  <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"; 
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
 crossorigin="anonymous" defer></script>
+  <script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"; 
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
 crossorigin="anonymous" defer></script>
+  <script 
src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"; 
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
 crossorigin="anonymous" defer></script>
+  <script 
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/prism.min.js"; 
defer></script>
+       <script 
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/autoloader/prism-autoloader.min.js";
 defer></script>
+
+  <script type="text/javascript">
+    window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new 
Date;
+    ga('create', 'UA-400821-1', 'auto');
+    ga('send', 'pageview');
+  </script>
+  <script async src="https://www.google-analytics.com/analytics.js";></script>
+</head>
+<body>
+  <!-- /// Navigation Start -->
+    <div id="navigation"><p><header>
+  <div class="container-fluid">
+    <div class="row">
+      <div class="col-12">
+        <nav class="navbar navbar-expand-xl navbar-light 
justify-content-between">
+          <a class="navbar-brand" href="/index.html">
+            <img src="/images/apache-tapestry-icon-dark.svg" width="60" 
alt="Apache Tapestry" title="Apache Tapestry">
+            <span>apache tapestry</span>
+          </a>
+          <button type="button" class="navbar-toggler" data-toggle="collapse" 
data-target="#navbarCollapse" aria-controls="navbarCollapse" 
aria-expanded="false" aria-label="Toggle navigation">
+            <span class="navbar-toggler-icon"></span>
+          </button>
+          <div class="collapse navbar-collapse" id="navbarCollapse">
+            <ul class="navbar-nav mx-auto"><li class="nav-item">
+                <a class="nav-link active" 
href="/getting-started.html">Getting Started</a>
+              </li><li class="nav-item">
+                <a class="nav-link active" 
href="/documentation.html">Documentation</a>
+              </li><li class="nav-item">
+                <a class="nav-link active" href="/download.html">Download</a>
+              </li><li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle active" 
id="communityNavbarDropdown" href="#" role="button" data-toggle="dropdown" 
aria-haspopup="true" aria-expanded="false">
+                  Community
+                </a>
+                <div class="dropdown-menu" 
aria-labelledby="communityNavbarDropdown">
+                  <a class="dropdown-item" href="/community.html">Mailing 
Lists</a>
+                  <a class="dropdown-item" 
href="https://stackoverflow.com/questions/tagged/tapestry";>StackOverflow</a>
+                  <a class="dropdown-item" href="/support.html">Support</a>
+                  <a class="dropdown-item" href="/community.html">Getting 
Involved</a>
+                  <a class="dropdown-item" 
href="https://cwiki.apache.org/confluence/pages/editpage.action?pageId=199530832";
 title="Edit this page (requires approval, just ask on the mailing list)">Edit 
this page</a>
+                  <div class="dropdown-divider"></div>
+                  <a class="dropdown-item" 
href="https://twitter.com/ApacheTapestry";>@ApacheTapestry</a>
+                  <a class="dropdown-item" 
href="https://twitter.com/hashtag/tapestry5";>#tapestry5</a>
+                </div>
+              </li><li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle active" 
id="developmentNavbarDropdown" href="#" role="button" data-toggle="dropdown" 
aria-haspopup="true" aria-expanded="false">
+                  Development
+                </a>
+                <div class="dropdown-menu" 
aria-labelledby="developmentNavbarDropdown">
+                  <a class="dropdown-item" 
href="https://gitbox.apache.org/repos/asf?p=tapestry-5.git";>Source Code</a>
+                  <a class="dropdown-item" 
href="https://issues.apache.org/jira/browse/TAP5";>Issues</a>
+                </div>
+              </li><li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle active" 
id="apacheNavbarDropdown" href="#" role="button" data-toggle="dropdown" 
aria-haspopup="true" aria-expanded="false">
+                  Apache
+                </a>
+                <div class="dropdown-menu" 
aria-labelledby="apacheNavbarDropdown">
+                  <a class="dropdown-item" 
href="https://www.apache.org/";>About Apache</a>
+                  <a class="dropdown-item" 
href="https://apachecon.com/?ref=royale.apache.org";>Events</a>
+                  <a class="dropdown-item" 
href="https://www.apache.org/foundation/sponsorship.html";>Sponsorship</a>
+                  <a class="dropdown-item" 
href="https://www.apache.org/licenses/LICENSE-2.0";>License</a>
+                  <a class="dropdown-item" 
href="https://www.apache.org/security/";>Security</a>
+                  <a class="dropdown-item" 
href="https://www.apache.org/foundation/thanks.html";>Thanks!</a>
+                </div>
+              </li></ul>
+            <form enctype="application/x-www-form-urlencoded" method="get" 
class="form-inline" action="search.html">
+              <input type="search" class="form-control search-input" name="q" 
placeholder="Search docs, issues, wikis and blogs" aria-label="Search">
+              <button type="submit" class="d-none">Search</button>
+            </form>
+          </div>
+        </nav>
+      </div>
+    </div>
+  </div>
+</header></p></div>
+  <!-- /// Navigation End -->
+
+  <article>
+    <div class="container-fluid">
+      <div class="container pt-5">
+        <div class="row">
+          <div class="col-12">
+                          <!-- /// Breadcrumb Start -->
+              <div id="breadcrumb" class="mb-2 text-small">
+                                <a href="index.html">Apache 
Tapestry</a>&nbsp;&gt;&nbsp;<a 
href="documentation.html">Documentation</a>&nbsp;&gt;&nbsp;<a 
href="user-guide.html">User Guide</a>&nbsp;&gt;&nbsp;<a 
href="rest-support-580.html">REST Support (5.8.0+)</a>
+              </div>
+              <!-- /// Breadcrumb End -->
+
+              <!-- /// Smallbanner Start -->
+                            <div id="smallbanner"><h1 class="title" 
id="title">REST Support (5.8.0+)</h1></div>
+              <!-- /// Smallbanner Start -->
+            
+            <!-- /// Content Start -->
+            <div id="content">
+                            <div id="ConfluenceContent"><h2 
id="RESTSupport(5.8.0+)-/*&lt;![CDATA[*/div.rbtoc1642965103666{padding:0px;}div.rbtoc1642965103666ul{list-style:disc;margin-left:0px;}div.rbtoc1642965103666li{margin-left:0px;padding-left:0px;}/*]]&gt;*/#RESTSupport(5.8.0+)-Overview#RESTSupport(5.8.0+)-OverviewWrit"><style
 type="text/css">/*<![CDATA[*/
+div.rbtoc1642965103666 {padding: 0px;}
+div.rbtoc1642965103666 ul {list-style: disc;margin-left: 0px;}
+div.rbtoc1642965103666 li {margin-left: 0px;padding-left: 0px;}
+
+/*]]>*/</style></h2><div class="toc-macro rbtoc1642965103666">
+<ul class="toc-indentation"><li><a 
href="#RESTSupport(5.8.0+)-"></a></li><li><a 
href="#RESTSupport(5.8.0+)-Overview">Overview</a></li><li><a 
href="#RESTSupport(5.8.0+)-WritingRESTendpoints">Writing REST 
endpoints</a></li><li><a 
href="#RESTSupport(5.8.0+)-Readingtherequestbodywith@RequestBody">Reading the 
request body with @RequestBody</a></li><li><a 
href="#RESTSupport(5.8.0+)-AnsweringRESTrequests">Answering REST requests</a>
+<ul class="toc-indentation"><li><a 
href="#RESTSupport(5.8.0+)-Contentresponses">Content responses</a></li><li><a 
href="#RESTSupport(5.8.0+)-Non-contentresponses">Non-content 
responses</a></li></ul>
+</li><li><a 
href="#RESTSupport(5.8.0+)-MappedEntityManagerservice">MappedEntityManager 
service</a></li><li><a 
href="#RESTSupport(5.8.0+)-IntegrationwithJacksonDatabindwithtapestry-rest-jackson">Integration
 with Jackson Databind with tapestry-rest-jackson</a></li><li><a 
href="#RESTSupport(5.8.0+)-AutomaticgenerationofOpenAPI3.0(Swagger)descriptions">Automatic
 generation of OpenAPI 3.0 (Swagger) descriptions</a>
+<ul class="toc-indentation"><li><a 
href="#RESTSupport(5.8.0+)-Customizingnames,summariesanddescriptionsusingmessagesandconfigurationsymbols">Customizing
 names, summaries and descriptions using messages and configuration 
symbols</a></li><li><a 
href="#RESTSupport(5.8.0+)-CustomizingtheconsumedandproducedMIMEcontenttypesofanendpointwith@RestInfo">Customizing
 the consumed and produced MIME content types of an endpoint with 
@RestInfo</a></li><li><a 
href="#RESTSupport(5.8.0+)-Furthercustomizations">Further 
customizations</a></li></ul>
+</li><li><a 
href="#RESTSupport(5.8.0+)-tapestry-openapi-viewer">tapestry-openapi-viewer</a></li></ul>
+</div><h2 id="RESTSupport(5.8.0+)-Overview">Overview</h2><p>Since version 
5.8.0, Tapestry provides out-of-the-box support for writing REST endpoints as 
regular event handler methods in page classes. They work in the same way the 
<code>activate</code> event (i.e. <code>onActivate()</code> methods) work, 
including how event handler method parameters work. The 
<code>@RequestBody</code> annotation was created so event handler methods can 
access the request body. The @StaticActivationContextValue annotation was 
created so you can write event handler methods that are only called when one or 
more parts of the URL path match given values. Both annotations are not 
REST-specific and can be used in any event handler method. A new 
subproject/JAR, tapestry-rest-jackson, automates the use of Jackson Databind to 
make JSON conversions. tapestry-Swagger/OpenAPI 3.0 descriptions are generated 
automatically and can be easily customized. A new subproject/JAR, 
tapestry-openapi-viewer, provides an out-of
 -the-box viewer for the generated OpenAPI description using Swagger UI. For a 
Tapestry REST support example project, check out <a class="external-link" 
href="https://github.com/thiagohp/tapestry-rest-example"; 
rel="nofollow">https://github.com/thiagohp/tapestry-rest-example</a>.</p><p>Some
 important warnings:</p><ol><li>Tapestry's REST support isn't an implementation 
of JAX-RS, so they expect any of its concepts to work here. It's REST 
implemented in a Tapestry way.</li><li><span style="letter-spacing: 
0.0px;">REST endpoint event handler methods in components are ignored just like 
<code>onActivate()</code> is.</span></li></ol><p>The following HTTP methods are 
supported:</p><div class="table-wrap"><table class="table table-bordered 
table-responsive"><colgroup span="1"><col span="1" style="width: 
21.3382%;"><col span="1" style="width: 30.9222%;"><col span="1" style="width: 
25.859%;"><col span="1" style="width: 21.8807%;"></colgroup><tbody><tr><th 
colspan="1" rowspan="1" class="confluen
 ceTh">HTTP method</th><th colspan="1" rowspan="1" 
class="confluenceTh">Tapestry event name</th><th colspan="1" rowspan="1" 
class="confluenceTh"><p>EventConstants</p><p>constant name</p></th><th 
colspan="1" rowspan="1" class="confluenceTh"><p>Event handler</p><p>method 
name</p></th></tr><tr><td colspan="1" rowspan="1" 
class="confluenceTd"><code>GET</code></td><td colspan="1" rowspan="1" 
class="confluenceTd"><code>httpGet</code></td><td colspan="1" rowspan="1" 
class="confluenceTd"><code>HTTP_GET</code></td><td colspan="1" rowspan="1" 
class="confluenceTd"><code>onHttpGet</code></td></tr><tr><td colspan="1" 
rowspan="1" class="confluenceTd"><code>POST</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>httpPost</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>HTTP_POST</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>onHttpPost</code></td></tr><tr><td 
colspan="1" rowspan="1" class="confluenceTd"><code>DELETE</code></td><td 
colspan="1" rowsp
 an="1" class="confluenceTd"><code>httpDelete</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>HTTP_DELETE</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>onHttpDelete</code></td></tr><tr><td 
colspan="1" rowspan="1" class="confluenceTd"><code>PUT</code></td><td 
colspan="1" rowspan="1" class="confluenceTd"><code>httpPut</code></td><td 
colspan="1" rowspan="1" class="confluenceTd"><code>HTTP_PUT</code></td><td 
colspan="1" rowspan="1" 
class="confluenceTd"><code>onHttpPut</code></td></tr><tr><td colspan="1" 
rowspan="1" class="confluenceTd"><code>HEAD</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>httpHead</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>HTTP_HEAD</code></td><td colspan="1" 
rowspan="1" class="confluenceTd"><code>onHttpHead</code></td></tr><tr><td 
colspan="1" rowspan="1" class="confluenceTd"><code>PATCH</code></td><td 
colspan="1" rowspan="1" class="confluenceTd"><code>httpPatch</code></td><td 
colspan="1"
  rowspan="1" class="confluenceTd"><code>HTTP_PATCH</code></td><td colspan="1" 
rowspan="1" 
class="confluenceTd"><code>onHttpPatch</code></td></tr></tbody></table></div><h2
 id="RESTSupport(5.8.0+)-WritingRESTendpoints">Writing REST 
endpoints</h2><p>Writing a REST endpoint in Tapestry is exactly the same as 
writing <code>onActivate()</code> method in a page class. Everything is the 
same: parameter handling, returned value processing, precedence rules, class 
inheritance, URLs, etc. If you know how to write <code>onActivate()</code> 
methods, you already know almost everything you need how to write a REST 
endpoint event handler.&#160; &#160;There are only 2 small differences between 
<code>onActivate()</code> and REST endpoint event handler 
methods:</p><ol><li>REST event handler methods are invoked after the 
<code>onActivate()</code>.&#160;</li><li>The event name is different, according 
to the HTTP method to be handled.</li></ol><p>So, for example, if you want a 
REST endpoint with URL /use
 rendpoint/[id], supposing the id is a <code>Long</code>, handling the 
<code>GET</code> HTTP method, you can just write the following page class and 
event handler:</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeContent panelContent pdl">
+<pre><code class="language-java">public class UserEndpoint {
+
+    Object onHttpGet(Long id) {
+        (...)
+    }
+       (...)
+}</code></pre>
+</div></div><p>The example above could also be written using the 
<code>@OnEvent</code> annotation and would work the same:</p><div class="code 
panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre><code class="language-java">public class UserEndpoint {
+
+    @OnEvent(EventConstants.HTTP_GET) 
+       Object getById(Long id) { // or any other method name
+        (...)
+    }
+       (...)
+}</code></pre>
+</div></div><h2 class="auto-cursor-target" 
id="RESTSupport(5.8.0+)-Readingtherequestbodywith@RequestBody">Reading the 
request body with <code>@RequestBody</code></h2><p>Many times, specially with 
POST, PUT and PATCH requests, the data is sent through the request body. To get 
this data, the event handler method needs to add a parameter with the 
<code>@RequestBody</code> annotation. It has a single property, 
<code>allowEmpty</code>, with <code>false</code> as its default value, which 
defines whether an empty request body is empty. If not, an exception will be 
thrown.</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeContent panelContent pdl">
+<pre><code class="language-java">@OnEvent(EventConstants.HTTP_PUT) 
+Object save(@RequestBody User user) {
+       (...)
+}
+
+@OnEvent(EventConstants.HTTP_PUT) 
+Object save(Long id, @RequestBody User user) {
+       (...)
+}</code></pre>
+</div></div><p>The following types are supported 
out-of-the-box:</p><ul><li><code>String</code></li><li><code>Reader</code></li><li><code>InputStream</code></li><li><code>JSONObject</code></li><li><code>JSONArray</code></li><li>Primitive
 types and their wrapper types</li></ul><p>The actual conversion logic is 
implemented in the <code>HttpRequestBodyConverter</code> service, which is 
defined is an ordered configuration of <code>HttpRequestBodyConverter</code> 
instances. The service calls all contributed instances until one of them 
returns a non-null value. If none of them returns a non-null value, it falls 
back to trying to find a coercion, direct or indirect, from 
<code>HttpServletRequest</code> to the desired type.&#160;</p><p>Here's one 
example of implementing an new <code>HttpRequestBodyConverter</code> then the 
code added to <code>AppModule</code> or any other Tapestry-IoC module class to 
have it used by <code>@RequestBody</code>:</p><div class="code panel pdl" 
style="border-wid
 th: 1px;"><div class="codeContent panelContent pdl">
+<pre><code class="language-java">/**
+ * Converts the body of HTTP requests to {@link User} instances. It delegates 
this task
+ * to {@link UserService#toObject(String)}.
+ */
+public class UserHttpRequestBodyConverter implements HttpRequestBodyConverter {
+    
+    private final UserService userService;
+
+    public UserHttpRequestBodyConverter(UserService userService) {
+        super();
+        this.userService = userService;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public &lt;T&gt; T convert(HttpServletRequest request, Class&lt;T&gt; 
type) {
+        T value = null;
+        // Check whether this converter handles the given type
+        if (User.class.equals(type)) {
+                       // Actual conversion logic
+            try {
+                value = (T) 
userService.toObject(IOUtils.toString(request.getReader()));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+ &#160; &#160; &#160; &#160;}
+        return value;
+    }
+
+}</code></pre>
+</div></div><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>UserHttpRequestBodyConverter contribution</b></div><div 
class="codeContent panelContent pdl">
+<pre><code class="language-java">    public static void 
contributeHttpRequestBodyConverter(
+            OrderedConfiguration&lt;HttpRequestBodyConverter&gt; 
configuration) {
+        
+        configuration.addInstance("User", UserHttpRequestBodyConverter.class); 
// automatic instantiation and dependency injection
+               // or configuration.add("User", new 
UserHttpRequestBodyConverter(...));
+    }</code></pre>
+</div></div><h2 id="RESTSupport(5.8.0+)-AnsweringRESTrequests">Answering REST 
requests</h2><p>Just like any other Tapestry event handler method, the returned 
value defines what gets to be sent to the user agent making the request. This 
logic is written in <code>ComponentEventResultProcessor</code> implementations, 
usually but not necessarily one per return type/class, which are contributed to 
the <code>ComponentEventResultProcessor</code> service. These implementations 
can also set additional HTTP headers and set the HTTP status code.</p><p>REST 
requests responses usually fall into 2 types: ones just returning HTTP status 
and and headers (for example, <code>HEAD</code> and <code>DELETE</code> 
requests) and ones returning that and also content (for example, 
<code>GET</code>, sometimes other methods too).</p><h3 
id="RESTSupport(5.8.0+)-Contentresponses">Content responses</h3><p>For content 
responses, Tapestry has out-of-the-box support for <code>StreamResponse</code> 
(mostly binary co
 ntent),&#160; <code>TextStreamResponse</code> (simple text content), 
<code>JSONArray</code> (since Tapestry 5.8.0) and <code>JSONObject</code> 
(since 5.8.0).&#160; Here's one example for adding support for a class, 
<code>User</code>, converting it to the JSON format:</p><div class="code panel 
pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre><code class="language-java">/**
+ * Handles {@link User} instances when they're returned by event handler 
methods.
+ * Heavily inspired by {@link JSONCollectionEventResultProcessor} from 
Tapestry itself.
+ */
+final public class UserComponentEventResultProcessor 
+        implements ComponentEventResultProcessor&lt;User&gt; {
+
+    private final Response response;
+
+    private final ContentType contentType;
+    
+    private final UserService userService;
+
+    public UserComponentEventResultProcessor(Response response,
+            @Symbol(TapestryHttpSymbolConstants.CHARSET) String outputEncoding,
+            UserService userService)    {
+        this.response = response;
+        this.userService = userService;
+        contentType = new 
ContentType(InternalConstants.JSON_MIME_TYPE).withCharset(outputEncoding);
+    }
+
+    public void processResultValue(User user) throws IOException
+    {
+        PrintWriter pw = response.getPrintWriter(contentType.toString());
+        pw.write(userService.toJsonString(user));
+        pw.close();
+               // You could also set extra HTTP headers or the status code here
+    }        
+}</code></pre>
+</div></div><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>UserComponentEventResultProcessor contribution</b></div><div 
class="codeContent panelContent pdl">
+<pre><code class="language-java">    public void 
contributeComponentEventResultProcessor(
+            MappedConfiguration&lt;Class, ComponentEventResultProcessor&gt; 
configuration) {
+        configuration.addInstance(User.class, 
UserComponentEventResultProcessor.class);
+    }</code></pre>
+</div></div><h3 class="auto-cursor-target" 
id="RESTSupport(5.8.0+)-Non-contentresponses">Non-content responses</h3><p>For 
responses without content, just HTTP status and headers, and also for simple 
String responses, Tapestry 5.8.0 introduced the <code>HttpStatus</code> class. 
You can create instances of it by either using its utility static methods that 
match HTTP status names like <code>ok()</code>, <code>created()</code>, 
<code>accepted()</code>,&#160;<code>notFound()</code> and <code>forbidden() 
</code>or using one of its constructors. In both cases, you can customize the 
response further by using a fluent interface with methods for header-specific 
methods like <code>withLocation(url)</code> and 
<code>withContentLocation(url)</code> or the generic 
<code>withHttpHeader(String name, String value)</code>. Check the 
<code>HttpStatus</code> JavaDoc for the full list of methods.</p><div 
class="code panel pdl" style="border-width: 1px;"><div class="codeContent 
panelContent pdl">
+<pre><code class="language-java">    @OnEvent(EventConstants.HTTP_PUT) 
+    Object save(@RequestBody User user) {
+        userService.save(user);
+        return HttpStatus.created()
+                .withContentLocation("Some URL")
+                .withHttpHeader("X-Something", "X-Value");
+    }
+
+</code></pre>
+</div></div><h2 class="auto-cursor-target" 
id="RESTSupport(5.8.0+)-MappedEntityManagerservice"><code>MappedEntityManager</code>
 service</h2><p>This is a service which provides a list of mapped entities. 
They're usually classes which are mapped to other formats like JSON and XML and 
used to represent data received or sent to or from external processes, for 
example REST endpoints. Contributions are done by package and all classes 
inside the contributed ones are considered mapped entities. The <code>[root 
package]. rest.entities</code> package is automatically contributed.</p><p>This 
service is used by Tapestry code, including the OpenAPI description generator 
and tapestry-rest-jackson, to know which classes should be considered part of 
the webapp's external APIs.</p><p>Here's an example contribution:</p><div 
class="code panel pdl" style="border-width: 1px;"><div class="codeContent 
panelContent pdl">
+<pre><code class="language-java">    public static void 
contributeMappedEntityManager(Configuration&lt;String&gt; configuration) {
+        configuration.add("com.example.rest.entities");
+    }</code></pre>
+</div></div><h2 class="auto-cursor-target" 
id="RESTSupport(5.8.0+)-IntegrationwithJacksonDatabindwithtapestry-rest-jackson">Integration
 with Jackson Databind with tapestry-rest-jackson</h2><p>JSON has been widely 
used as a data interchange format in REST endpoints, while Jackson Databind is 
maybe the mostly used Java library for JSON mapping. 
tapestry-rest-jackson&#160;</p><p>automates the usage of Jackson Databind in 
Tapestry, defining an <code>ObjectMapperSource</code>&#160;service, 
contributing <code>ComponentEventResultProcessor</code> and 
<code>HttpRequestBodyConverter</code> implementations&#160; for&#160; all 
mapped entity classes (as defined by the <code>MappedEntityManager</code> 
service) and implementing the generation of JSON schemas for OpenAPI 
descriptions of mapped entity classes using <a class="external-link" 
href="https://victools.github.io/jsonschema-generator/"; 
rel="nofollow">victools/jsonschema-generator</a>.&#160;</p><p>The 
<code>ObjectMapperSource</code> service
  defines how tapestry-rest-jackson will get an <code>ObjectMapper</code> 
instance to use for a given entity class. It has an ordered configuration of 
<code>ObjectMapperSource</code>. It has a single method, <code>ObjectMapper 
get(Class&lt;?&gt; clasz)</code>. When it's called, it goes through all the 
contributed instances calling the same method on them until one returns a 
non-null value. When a non-null value is returned, the service method returns 
it. If none is found, the fallback is always returning the same object returned 
by <code>new ObjectMapper()</code>. Any customizations to the 
<code>ObjectMapper</code> instance should be done as contributions to the 
<code>ObjectMapperSource</code> service. Here's one example that defines which 
date format should be used for all entity classes:</p><div class="code panel 
pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre><code class="language-java">    /**
+     * Example of customizing the Jackson Databinding's {@link ObjectMapper}, 
specifically the
+     * date format.
+     */
+    public static void 
contributeObjectMapperSource(OrderedConfiguration&lt;ObjectMapperSource&gt; 
configuration) {
+        configuration.add("Custom", new CustomObjectMapperSource());
+    }
+    
+    private static final class CustomObjectMapperSource implements 
ObjectMapperSource {
+
+        final private ObjectMapper mapper;
+        
+        public CustomObjectMapperSource() {
+            mapper = new ObjectMapper(/* ... */);
+            mapper.setDateFormat(new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"));
+            // Any other customizations go here.
+        }
+        
+        @Override
+        public ObjectMapper get(Class&lt;?&gt; clasz) {
+            // You could check the class to provide class-specific 
ObjectMapper 
+            // instances here, but this isn't the case for this example
+            return mapper;
+        }
+        
+    }
+
+</code></pre>
+</div></div><h2 
id="RESTSupport(5.8.0+)-AutomaticgenerationofOpenAPI3.0(Swagger)descriptions">Automatic
 generation of OpenAPI 3.0 (Swagger) descriptions</h2><p>Tapestry provides an 
out-of-the-box OpenAPI 3.0 (Swagger) description generator driven by the REST 
endpoints event handlers (i.e. the ones handling the events listed in the table 
in the top of this page), configuration symbols and internationalization 
messages (i.e. app.properties). The description is in the JSON 
format.</p><p>It's disabled by default. It's enabled by setting the 
<code>tapestry.publish-openapi-description</code> 
(<code>SymbolConstants.PUBLISH_OPENAPI_DEFINITON</code>) configuration symbol 
to <code>true</code>. If enabled, it will be available at the 
<code>/openapi.json</code> URL. This is configurable by using the 
<code>tapestry.openapi-description-path</code> 
(<code>SymbolConstants.OPENAPI_DESCRIPTION_PATH</code>) configuration 
symbol.&#160;</p><p>All the entity classes returned by 
MappedEntityManager.getEnt
 ities() are automatically added to the <a class="external-link" 
href="https://swagger.io/docs/specification/data-models/#reuse"; 
rel="nofollow">schemas</a> section of the generated description. &#160;</p><h3 
id="RESTSupport(5.8.0+)-Customizingnames,summariesanddescriptionsusingmessagesandconfigurationsymbols">Customizing
 names, summaries and descriptions using messages and configuration 
symbols</h3><p>Summaries, names and descriptions and messages are taken from 
messages first, configuration symbol second, except for the OpenAPI version, 
which is only taken from the <code>tapestry.openapi-version</code> 
(<code>SymbolConstants.OPENAPI_VERSION</code>) symbol and has a default value 
of <code>3.0.0</code>. Given a message key, the corresponding configuration 
symbol is <code>tapestry.[message key]</code> format.</p><p>HTTP method names 
are lowercased.</p><p>When building a message key, if it's based on a path, the 
starting slash is not removed. For example, the summary for the <code>/some
 thing</code> endpoint for the POST method is 
<code>openapi./something.post.summary</code>.</p><p>You can see which messages 
and symbols are being looked up by setting the debug level of the 
<code>org.apache.tapestry5.internal.services.rest.DefaultOpenApiDescriptionGenerator</code>
 class to <code>DEBUG</code>.</p><h3 
id="RESTSupport(5.8.0+)-CustomizingtheconsumedandproducedMIMEcontenttypesofanendpointwith@RestInfo">Customizing
 the consumed and produced MIME content types of an endpoint with 
<code>@RestInfo</code></h3><p>The <code>@RestInfo</code>&#160;annotation allows 
you to define, just for OpenAPI description generation purposes, what are the 
accepted MIME content types accepted by a REST endpoint event handler method, 
the produced types and what's the actual returned value type of the method when 
its execution succeeds. Many REST event handler methods usually have a return 
type of <code>Object</code> so it can return an <code>HttpStatus</code> 
instance of something goes wrong and
  a mapped entity class entity when the call is successful.</p><p>Here's one 
example:&#160;</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeContent panelContent pdl">
+<pre><code class="language-java">@RestInfo(produces = "text/plain", produces = 
"application/json", returnType = User.class)
+Object onHttpGet(@RequestBody Long id) {
+    User user = ...;
+       if (!notFound) {
+        return HttpStatus.notFound()
+    }
+    else {
+        return user;    
+    }
+}</code></pre>
+</div></div><h3 id="RESTSupport(5.8.0+)-Furthercustomizations">Further 
customizations</h3><p>The generated description can be further customized by 
implementing the <code>OpenApiDescriptionGenerator</code> interface and 
contributing it to the <code>OpenApiDescriptionGenerator</code> service. The 
<code>JSONObject generate(JSONObject documentation)</code> method will receive 
the generated description and it can be changed by using the 
<code>JSONObject</code> methods. The return value should be the same object 
received as a parameter.</p><h2 
id="RESTSupport(5.8.0+)-tapestry-openapi-viewer">tapestry-openapi-viewer</h2><p>The
 tapestry-openapi-viewer Tapestry subproject/JAR embeds the open source <a 
class="external-link" href="https://swagger.io/tools/swagger-ui/"; 
rel="nofollow">Swagger-UI</a> OpenAPI/Swagger and makes it available at the 
<code>/openapiviewer</code> URL. No configuration is needed other than 
including the JAR in the classpath.</p><p></p></div>
+            </div>
+            <!-- /// Content End -->
+          </div>
+        </div>
+      </div>
+    </div>
+  </article>
+
+  <!-- /// Footer Start -->
+    <div id="footer"><p>Apache Tapestry, Tapestry, Apache, the Apache feather 
logo, and the Apache Tapestry project logo are trademarks of The Apache 
Software Foundation.</p><p><br clear="none"><footer class="py-3">
+  <div class="container-fluid">
+    <div class="container">
+      <div class="row">
+        <div class="col-4 col-lg-2">
+          <span class="font-weight-bold">Apache Tapestry</span>
+          <ul><li><a href="index.html">Home</a></li><li><a 
href="download.html">Download</a></li><li><a 
href="about.html">Team</a></li><li><a 
href="https://www.apache.org/licenses/LICENSE-2.0";>License</a></li></ul>
+          <span class="font-weight-bold">Documentation</span>
+          <ul><li><a href="introduction.html">Introduction</a></li><li><a 
href="principles.html">Principles</a></li><li><i class="fas fa-play"></i> <a 
href="getting-started.html">Getting Started</a></li><li><i class="fas 
fa-play"></i> <a href="user-guide.html">User Guide</a></li><li><i class="fas 
fa-book"></i> <a href="documentation.html">Docs</a></li><li><i class="fas 
fa-book"></i> <a href="component-reference.html">Component 
Reference</a></li><li><i class="fas fa-book"></i> <a 
href="current/apidocs">Apidocs</a></li><li><a 
href="frequently-asked-questions.html">FAQ</a></li></ul>
+        </div>
+        <div class="col-4 col-lg-2">
+          <span class="font-weight-bold">Community</span>
+          <ul><li><i class="fas fa-envelope-open-text"></i> <a 
href="community.html">Mailing Lists</a></li><li><i class="fab 
fa-stack-overflow"> </i> <a 
href="https://stackoverflow.com/questions/tagged/tapestry";>StackOverflow</a></li><li><a
 href="support.html">Support</a></li><li><a href="community.html">Getting 
Involved</a></li><li><a 
href="https://cwiki.apache.org/confluence/pages/editpage.action?pageId=199530832";
 title="Edit this page (requires approval, just ask on the mailing list)">Edit 
this page</a></li></ul>
+          <span class="font-weight-bold">Social</span>
+          <ul><li><i class="fab fa-twitter"></i> <a 
href="https://twitter.com/ApacheTapestry";>@ApacheTapestry</a></li><li><i 
class="fas fa-hashtag"></i> <a 
href="https://twitter.com/hashtag/tapestry5";>#tapestry5</a></li></ul>
+          <span class="font-weight-bold">Development</span>
+          <ul><li><i class="fab fa-git"></i> <a 
href="https://gitbox.apache.org/repos/asf?p=tapestry-5.git";>Source 
Code</a></li><li><i class="fab fa-jira"></i> <a 
href="https://issues.apache.org/jira/browse/TAP5";>Issues</a></li></ul>
+        </div>
+        <div class="col-4 col-lg-2">
+          <span class="font-weight-bold">Apache</span>
+          <ul><li><a href="https://www.apache.org/";>About 
Apache</a></li><li><a 
href="https://apachecon.com/?ref=royale.apache.org";>Events</a></li><li><a 
href="https://www.apache.org/foundation/sponsorship.html";>Sponsorship</a></li><li><a
 href="https://www.apache.org/licenses/LICENSE-2.0";>License</a></li><li><a 
href="https://www.apache.org/security/";>Security</a></li><li><a 
href="https://www.apache.org/foundation/thanks.html";>Thanks!</a></li></ul>
+        </div>
+        <div class="col-md-12 col-lg-6 clearfix">
+          <span class="font-weight-bold d-block">About us</span>
+          <p class="float-right ml-3 mb-0"><img 
src="images/apache-tapestry-icon-light.svg" width="100" alt="Apache Tapestry" 
title="Apache Tapestry"></p>
+          <p><a href="https://tapestry.apache.org/";>Apache Tapestry&#8482;</a> 
is an open-source component-oriented framework for creating dynamic, robust, 
highly scalable web applications in Java.</p>
+          <p>Tapestry complements and builds upon the standard Java Servlet 
API, and so it works in any servlet container or application server.</p>
+          <p class="float-right ml-3 mb-0"><img 
src="images/apache-powered-by.svg" width="100" alt="Apache PoweredBy" 
title="Apache PoweredBy"></p>
+          <p>
+            <a href="https://tapestry.apache.org/";>Apache Tapestry&#8482;</a>, 
<a href="https://www.apache.org/";>Apache&#8482;</a> and the <a 
href="https://www.apache.org/foundation/press/kit/";>Apache feather 
logo&#8482;</a>
+            are trademarks of The Apache Software Foundation. All other marks 
mentioned may be trademarks or registered trademarks of their respective owners.
+          </p>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-12">
+          Copyright &#169; 2020 The Apache Software Foundation, Licensed under 
the <a href="https://www.apache.org/licenses/LICENSE-2.0";>Apache License, 
Version 2.0</a>.
+        </div>
+      </div>
+    </div>
+  </div>
+</footer><br clear="none"></p><p><br clear="none"></p></div>
+  <!-- /// Footer End -->
+</body>
+</html>


Reply via email to