This is an automated email from the ASF dual-hosted git repository. thiagohp pushed a commit to branch rest in repository https://gitbox.apache.org/repos/asf/tapestry-5.git
commit 879c71d3204cacd95110a71c43aaa61ebea8deff Author: Thiago H. de Paula Figueiredo <[email protected]> AuthorDate: Wed Dec 1 22:22:28 2021 -0300 TAP5-2696: adding tapestry-rest-jackson --- build.gradle | 4 +- settings.gradle | 2 +- .../rest/DefaultOpenApiDescriptionGenerator.java | 1 + .../services/rest/OpenApiTypeDescriber.java | 2 +- tapestry-openapi-viewer/LICENSE-swagger-ui.txt | 202 +++++++++++++++++++++ tapestry-openapi-viewer/NOTICE.txt | 6 +- tapestry-rest-jackson/LICENSE.txt | 202 +++++++++++++++++++++ .../NOTICE.txt | 0 tapestry-rest-jackson/build.gradle | 14 ++ .../services/ObjectMapperSource.java | 41 +++++ .../internal/FallbackObjectMapperSource.java | 39 ++++ .../JacksonComponentEventResultProcessor.java | 62 +++++++ .../internal/JacksonHttpRequestBodyConverter.java | 53 ++++++ .../internal/JacksonOpenApiTypeDescriber.java | 71 ++++++++ .../rest/jackson/modules/RestJacksonModule.java | 130 +++++++++++++ .../tapestry5/rest/jackson/test/pages/Index.java | 71 ++++++++ .../rest/jackson/test/rest/entities/Attribute.java | 51 ++++++ .../rest/jackson/test/rest/entities/User.java | 74 ++++++++ .../rest/jackson/test/services/AppModule.java | 25 +++ .../src/test/webapp/WEB-INF/web.xml | 35 ++++ 20 files changed, 1078 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 829a353..b73e698 100755 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,9 @@ project.ext.versions = [ hibernate: "5.4.32.Final", slf4j: "1.7.25", geb: "2.0", - selenium: "3.141.59" + selenium: "3.141.59", + jackson: "2.13.0", + jsonschemaGenerator: "4.20.0" ] ext.continuousIntegrationBuild = Boolean.getBoolean("ci") diff --git a/settings.gradle b/settings.gradle index 2884936..a3705d0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,4 +9,4 @@ include "tapestry-test-data", 'tapestry-internal-test', "tapestry-ioc-junit" include "tapestry-webresources", "tapestry-runner", "tapestry-test-constants" include "tapestry-ioc-jcache", "beanmodel", "commons", "genericsresolver-guava", "tapestry-version-migrator" // include "tapestry-cdi" -include "tapestry-spock", "tapestry-openapi-viewer" +include "tapestry-spock", "tapestry-openapi-viewer", "tapestry-rest-jackson" diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java index 5f556bf..0e814c5 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java @@ -640,6 +640,7 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen else { path = path.substring(basePath.length() - 1); // keep the slash + path = path.replace("//", "/"); // remove possible double slashes } return path; } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/rest/OpenApiTypeDescriber.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/rest/OpenApiTypeDescriber.java index 1269879..01d4d88 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/rest/OpenApiTypeDescriber.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/rest/OpenApiTypeDescriber.java @@ -40,7 +40,7 @@ public interface OpenApiTypeDescriber /** * Describes a REST event handler method return type. - * @param descriptiona {@link JSONObject} containing the description of a path. + * @param descriptiona {@link JSONObject} containing the description of a path response. * @param parameter the event handler method itself. */ void describeReturnType(final JSONObject description, Method method); diff --git a/tapestry-openapi-viewer/LICENSE-swagger-ui.txt b/tapestry-openapi-viewer/LICENSE-swagger-ui.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/tapestry-openapi-viewer/LICENSE-swagger-ui.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/tapestry-openapi-viewer/NOTICE.txt b/tapestry-openapi-viewer/NOTICE.txt index 789c880..fd3593f 100644 --- a/tapestry-openapi-viewer/NOTICE.txt +++ b/tapestry-openapi-viewer/NOTICE.txt @@ -1,5 +1,3 @@ -This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). +This product includes software developed by The Apache Software Foundation (http://www.apache.org/). -This software depends on the Swagger UI licensed -under the Apache License Version 2.0, January 2014 +This product includes Swagger UI licensed, under the Apache License Version 2.0, January 2014 diff --git a/tapestry-rest-jackson/LICENSE.txt b/tapestry-rest-jackson/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/tapestry-rest-jackson/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/tapestry-openapi-viewer/NOTICE.txt b/tapestry-rest-jackson/NOTICE.txt similarity index 100% copy from tapestry-openapi-viewer/NOTICE.txt copy to tapestry-rest-jackson/NOTICE.txt diff --git a/tapestry-rest-jackson/build.gradle b/tapestry-rest-jackson/build.gradle new file mode 100644 index 0000000..7c9498a --- /dev/null +++ b/tapestry-rest-jackson/build.gradle @@ -0,0 +1,14 @@ +description = "Support for using Jackson Databind with the Tapestry REST support" + +dependencies { + compile project(':tapestry-core') + compile "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + compile "com.github.victools:jsonschema-generator:${versions.jsonschemaGenerator}" + provided "javax.servlet:javax.servlet-api:${versions.servletapi}" +} + +jar { + manifest { + attributes 'Tapestry-Module-Classes': 'org.apache.tapestry5.rest.jackson.services.RestJacksonModule.' + } +} diff --git a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/jacksondatabind/services/ObjectMapperSource.java b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/jacksondatabind/services/ObjectMapperSource.java new file mode 100644 index 0000000..f75c530 --- /dev/null +++ b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/jacksondatabind/services/ObjectMapperSource.java @@ -0,0 +1,41 @@ +// Licensed 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. +package org.apache.tapestry5.jacksondatabind.services; + +import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration; +import org.apache.tapestry5.services.rest.MappedEntityManager; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * <p> + * Service that provides {@link ObjectMapper} instances for specific mapped entity classes. + * <p> + * <p> + * It's defined as an ordered configuration of {@link ObjectMapperSource} instances. + * Calls to {@link #get(Class)} will call the same method in contributed instances until + * one returns a non-null {@link ObjectMapper} instance. + * </p> + * @see + * @see MappedEntityManager + * @since 5.8.0 + */ +@UsesOrderedConfiguration(ObjectMapperSource.class) +public interface ObjectMapperSource +{ + + /** + * Provides the {@linkplain ObjectMapper} to be used for a given mapped entity class. + */ + ObjectMapper get(Class<?> clasz); + +} diff --git a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/FallbackObjectMapperSource.java b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/FallbackObjectMapperSource.java new file mode 100644 index 0000000..2d1b056 --- /dev/null +++ b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/FallbackObjectMapperSource.java @@ -0,0 +1,39 @@ +// 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. +package org.apache.tapestry5.rest.jackson.internal; + +import org.apache.tapestry5.jacksondatabind.services.ObjectMapperSource; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * {@link ObjectMapperSource} implementation that always returns the + * same object returned by instantiating {@link ObjectMapper}. + * @since 5.8.0 + */ +public final class FallbackObjectMapperSource implements ObjectMapperSource +{ + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public ObjectMapper get(Class<?> clasz) + { + return objectMapper; + } + +} \ No newline at end of file diff --git a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonComponentEventResultProcessor.java b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonComponentEventResultProcessor.java new file mode 100644 index 0000000..8f3b6b1 --- /dev/null +++ b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonComponentEventResultProcessor.java @@ -0,0 +1,62 @@ +// Licensed 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. +package org.apache.tapestry5.rest.jackson.internal; + +import java.io.IOException; +import java.io.PrintWriter; + +import org.apache.tapestry5.http.ContentType; +import org.apache.tapestry5.http.TapestryHttpSymbolConstants; +import org.apache.tapestry5.http.services.Response; +import org.apache.tapestry5.internal.InternalConstants; +import org.apache.tapestry5.ioc.annotations.Symbol; +import org.apache.tapestry5.jacksondatabind.services.ObjectMapperSource; +import org.apache.tapestry5.services.ComponentEventResultProcessor; +import org.apache.tapestry5.services.rest.MappedEntityManager; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Handles mapped entity class instances using Jackson Databind + * when they're returned by event handler methods. It uses the {@link ObjectMapper} + * instance provided by {@link ObjectMapperSource}. + * + * @see MappedEntityManager + * @see ObjectMapperSource + * @since 5.8.0 + */ +public class JacksonComponentEventResultProcessor<T> implements ComponentEventResultProcessor<T> { + + private final Response response; + + private final ContentType contentType; + + private final ObjectMapperSource objectMapperSource; + + private final Class<T> entityClass; + + public JacksonComponentEventResultProcessor(Class<T> entityClass, Response response, + @Symbol(TapestryHttpSymbolConstants.CHARSET) String outputEncoding, + ObjectMapperSource objectMapperSource) + { + this.response = response; + this.objectMapperSource = objectMapperSource; + this.entityClass = entityClass; + contentType = new ContentType(InternalConstants.JSON_MIME_TYPE).withCharset(outputEncoding); + } + + public void processResultValue(T object) throws IOException { + PrintWriter pw = response.getPrintWriter(contentType.toString()); + pw.write(objectMapperSource.get(entityClass).writeValueAsString(object)); + pw.close(); + } +} diff --git a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonHttpRequestBodyConverter.java b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonHttpRequestBodyConverter.java new file mode 100644 index 0000000..874a22e --- /dev/null +++ b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonHttpRequestBodyConverter.java @@ -0,0 +1,53 @@ +// Licensed 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. +package org.apache.tapestry5.rest.jackson.internal; + +import java.io.IOException; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.tapestry5.commons.internal.util.TapestryException; +import org.apache.tapestry5.http.services.HttpRequestBodyConverter; +import org.apache.tapestry5.jacksondatabind.services.ObjectMapperSource; +import org.apache.tapestry5.services.rest.MappedEntityManager; + +public class JacksonHttpRequestBodyConverter implements HttpRequestBodyConverter { + + final private Set<Class<?>> entities; + + final private ObjectMapperSource objectMapperSource; + + public JacksonHttpRequestBodyConverter(MappedEntityManager mappedEntityManager, + ObjectMapperSource objectMapperSource) + { + this.entities = mappedEntityManager.getEntities(); + this.objectMapperSource = objectMapperSource; + } + + @Override + public <T> T convert(HttpServletRequest request, Class<T> type) { + + T value = null; + if (entities.contains(type)) + { + try { + value = objectMapperSource.get(type).readValue(request.getReader(), type); + } catch (IOException e) { + throw new TapestryException("Exception while converting HTTP request body into " + type.getName(), e); + } + } + + return value; + } + +} diff --git a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonOpenApiTypeDescriber.java b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonOpenApiTypeDescriber.java new file mode 100644 index 0000000..990e87d --- /dev/null +++ b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonOpenApiTypeDescriber.java @@ -0,0 +1,71 @@ +// 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. +package org.apache.tapestry5.rest.jackson.internal; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Set; + +import org.apache.tapestry5.json.JSONObject; +import org.apache.tapestry5.services.rest.MappedEntityManager; +import org.apache.tapestry5.services.rest.OpenApiTypeDescriber; + +import com.github.victools.jsonschema.generator.SchemaGenerator; + +/** + * {@link OpenApiTypeDescriber} implementation using Java JSONSchema Generator, + * by default generating JSON Schema 2019-09. + * @see https://github.com/victools/jsonschema-generator/tree/master/jsonschema-generator. + */ +public class JacksonOpenApiTypeDescriber implements OpenApiTypeDescriber +{ + + final private SchemaGenerator schemaGenerator; + final Set<Class<?>> entities; + public JacksonOpenApiTypeDescriber(SchemaGenerator schemaGenerator, MappedEntityManager mappedEntityManager) { + super(); + this.schemaGenerator = schemaGenerator; + entities = mappedEntityManager.getEntities(); + } + + @Override + public void describe(JSONObject description, Parameter parameter) + { + } + + @Override + public void describeReturnType(JSONObject description, Method method) + { + } + + @Override + public void describeSchema(Class<?> entity, JSONObject schemas) + { + if (entities.contains(entity)) + { + final JSONObject schema = new JSONObject( + schemaGenerator.generateSchema(entity).toString()); + schema.remove("$schema"); + schemas.put(getSchemaName(entity), schema); + } + } + + private String getSchemaName(final Class<?> returnType) { + return returnType.getSimpleName(); + } + +} diff --git a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/modules/RestJacksonModule.java b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/modules/RestJacksonModule.java new file mode 100644 index 0000000..b70f3c2 --- /dev/null +++ b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/modules/RestJacksonModule.java @@ -0,0 +1,130 @@ +// Copyright 2021 The Apache Software Foundation +// +// Licensed 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. + +package org.apache.tapestry5.rest.jackson.modules; + +import java.util.List; + +import org.apache.tapestry5.commons.Configuration; +import org.apache.tapestry5.commons.MappedConfiguration; +import org.apache.tapestry5.commons.OrderedConfiguration; +import org.apache.tapestry5.http.TapestryHttpSymbolConstants; +import org.apache.tapestry5.http.internal.TapestryHttpInternalConstants; +import org.apache.tapestry5.http.services.HttpRequestBodyConverter; +import org.apache.tapestry5.http.services.Response; +import org.apache.tapestry5.ioc.annotations.Symbol; +import org.apache.tapestry5.ioc.services.ChainBuilder; +import org.apache.tapestry5.jacksondatabind.services.ObjectMapperSource; +import org.apache.tapestry5.rest.jackson.internal.FallbackObjectMapperSource; +import org.apache.tapestry5.rest.jackson.internal.JacksonComponentEventResultProcessor; +import org.apache.tapestry5.rest.jackson.internal.JacksonHttpRequestBodyConverter; +import org.apache.tapestry5.rest.jackson.internal.JacksonOpenApiTypeDescriber; +import org.apache.tapestry5.services.ComponentEventResultProcessor; +import org.apache.tapestry5.services.rest.MappedEntityManager; +import org.apache.tapestry5.services.rest.OpenApiTypeDescriber; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.victools.jsonschema.generator.OptionPreset; +import com.github.victools.jsonschema.generator.SchemaGenerator; +import com.github.victools.jsonschema.generator.SchemaGeneratorConfig; +import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder; +import com.github.victools.jsonschema.generator.SchemaVersion; + +/** + * Defines services and and contributions for the Tapestry integration with Jackson Databind. + * Besides contributing a fallback {@link ObjectMapperSource}, it also creates a + * {@link ComponentEventResultProcessor} for all classes returned by + * {@link MappedEntityManager#getEntities()}. + * @since 5.8.0 + */ +public class RestJacksonModule +{ + + /** + * Contributes {@link FallbackObjectMapperSource} (contribution id <code>Fallback</code>) + * so we guarantee there's always an {@link ObjectMapper} provided for any type. + */ + public static void contributeObjectMapperSource(OrderedConfiguration<ObjectMapperSource> configuration) + { + configuration.addInstance("Fallback", FallbackObjectMapperSource.class); + } + + /** + * Adds a (entity class, JacksonComponentEventResultProcessor) for each entity class + * returned by {@link MappedEntityManager#getEntities()}. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void contributeComponentEventResultProcessor( + MappedConfiguration<Class, ComponentEventResultProcessor> configuration, + MappedEntityManager mappedEntityManager, + Response response, + ObjectMapperSource objectMapperSource, + @Symbol(TapestryHttpSymbolConstants.CHARSET) String outputEncoding) + { + for (Class entityClass : mappedEntityManager.getEntities()) + { + configuration.add(entityClass, + new JacksonComponentEventResultProcessor(entityClass, response, outputEncoding, objectMapperSource)); + } + } + + /** + * Contributes the package "<root>.rest.entities" to the configuration, + * so that it will be scanned for mapped entity classes. + */ + public static void contributeMappedEntityManager(Configuration<String> configuration, + @Symbol(TapestryHttpInternalConstants.TAPESTRY_APP_PACKAGE_PARAM) String appRootPackage) + { + configuration.add(appRootPackage + ".rest.entities"); + } + + /** + * Contributes {@link JacksonHttpRequestBodyConverter} to the {@link HttpRequestBodyConverter} service. + */ + public static void contributeHttpRequestBodyConverter( + OrderedConfiguration<HttpRequestBodyConverter> configuration) { + configuration.addInstance("Jackson", JacksonHttpRequestBodyConverter.class); + } + + /** + * Builds the {@link ObjectMapperSource} service. + */ + public static ObjectMapperSource buildObjectMapperSource( + List<ObjectMapperSource> configuration, + ChainBuilder chainBuilder) + { + return chainBuilder.build(ObjectMapperSource.class, configuration); + } + + /** + * Provides the default {@link SchemaGenerator} instance with a default configuration. + */ + public static SchemaGenerator buildSchemaGenerator() + { + SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder( + SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON); + SchemaGeneratorConfig config = configBuilder.build(); + return new SchemaGenerator(config); + } + + /** + * Contributes {@link JacksonOpenApiType} to the {@link OpenApiTypeDescriber} service + * to generate J. + */ + public static void contributeOpenApiTypeDescriber(OrderedConfiguration<OpenApiTypeDescriber> configuration) + { + configuration.addInstance("Jackson", JacksonOpenApiTypeDescriber.class); + } + +} diff --git a/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/pages/Index.java b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/pages/Index.java new file mode 100644 index 0000000..7b53a6e --- /dev/null +++ b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/pages/Index.java @@ -0,0 +1,71 @@ +// Licensed 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. +package org.apache.tapestry5.rest.jackson.test.pages; + +import java.io.UnsupportedEncodingException; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import org.apache.tapestry5.EventConstants; +import org.apache.tapestry5.annotations.OnEvent; +import org.apache.tapestry5.annotations.RequestBody; +import org.apache.tapestry5.annotations.RestInfo; +import org.apache.tapestry5.annotations.StaticActivationContextValue; +import org.apache.tapestry5.ioc.annotations.Inject; +import org.apache.tapestry5.rest.jackson.test.rest.entities.Attribute; +import org.apache.tapestry5.rest.jackson.test.rest.entities.User; +import org.apache.tapestry5.services.HttpStatus; +import org.apache.tapestry5.services.PageRenderLinkSource; + +public class Index +{ + + private static final Set<User> USERS = new HashSet<>(); + + @Inject private PageRenderLinkSource pageRenderLinkSource; + + @RestInfo(returnedType = User.class) + @OnEvent(EventConstants.HTTP_GET) + public Object getUserByEmail(String email) + { + final Optional<User> user = USERS.stream().filter(u -> email.equals(u.getEmail())).findFirst(); + return user.isPresent() ? user.get() : HttpStatus.notFound(); + } + + public Object onHttpPut(@RequestBody User user) throws UnsupportedEncodingException + { + HttpStatus status; + if (!USERS.contains(user)) + { + USERS.add(user); + status = HttpStatus.created(); + } + else + { + status = HttpStatus.ok(); + } + return status.withContentLocation( + pageRenderLinkSource.createPageRenderLinkWithContext(Index.class, user.getEmail())); + } + + @OnEvent(EventConstants.HTTP_GET) + public User getExample(@StaticActivationContextValue("example") String example) + { + User user = new User(); + user.setEmail("[email protected]"); + user.setName("Fulano da Silva"); + user.getAttributes().add(new Attribute("favoriteColor", "blue")); + return user; + } + +} diff --git a/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/rest/entities/Attribute.java b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/rest/entities/Attribute.java new file mode 100644 index 0000000..769bdcc --- /dev/null +++ b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/rest/entities/Attribute.java @@ -0,0 +1,51 @@ +// Licensed 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. +package org.apache.tapestry5.rest.jackson.test.rest.entities; + +public class Attribute +{ + + private String name; + + private String value; + + public Attribute() + { + + } + + public Attribute(String name, String value) { + super(); + this.name = name; + this.value = value; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) + { + this.value = value; + } + +} diff --git a/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/rest/entities/User.java b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/rest/entities/User.java new file mode 100644 index 0000000..b0ad3f0 --- /dev/null +++ b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/rest/entities/User.java @@ -0,0 +1,74 @@ +// Licensed 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. +package org.apache.tapestry5.rest.jackson.test.rest.entities; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class User +{ + + private String email; + + private String name; + + private List<Attribute> attributes = new ArrayList<>(); + + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public List<Attribute> getAttributes() + { + return attributes; + } + + public void setAttributes(List<Attribute> attributes) + { + this.attributes = attributes; + } + + @Override + public int hashCode() { + return Objects.hash(email, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof User)) { + return false; + } + User other = (User) obj; + return Objects.equals(email, other.email) && Objects.equals(name, other.name); + } + +} diff --git a/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/services/AppModule.java b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/services/AppModule.java new file mode 100644 index 0000000..d7c19e9 --- /dev/null +++ b/tapestry-rest-jackson/src/test/java/org/apache/tapestry5/rest/jackson/test/services/AppModule.java @@ -0,0 +1,25 @@ +// Licensed 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. +package org.apache.tapestry5.rest.jackson.test.services; + +import org.apache.tapestry5.SymbolConstants; +import org.apache.tapestry5.commons.MappedConfiguration; + +public class AppModule +{ + public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration) + { + configuration.add(SymbolConstants.PUBLISH_OPENAPI_DEFINITON, true); + configuration.add(SymbolConstants.PRODUCTION_MODE, false); + } + +} diff --git a/tapestry-rest-jackson/src/test/webapp/WEB-INF/web.xml b/tapestry-rest-jackson/src/test/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d719784 --- /dev/null +++ b/tapestry-rest-jackson/src/test/webapp/WEB-INF/web.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2021 The Apache Software Foundation + + Licensed 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. +--> + +<!DOCTYPE web-app + PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" + "http://java.sun.com/dtd/web-app_2_3.dtd"> +<web-app> + <display-name>Integration Test App 1</display-name> + <context-param> + <param-name>tapestry.app-package</param-name> + <param-value>org.apache.tapestry5.rest.jackson.test</param-value> + </context-param> + <filter> + <filter-name>app</filter-name> + <filter-class>org.apache.tapestry5.TapestryFilter</filter-class> + </filter> + <filter-mapping> + <filter-name>app</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> +</web-app>
