├── .editorconfig ├── .github ├── settings.xml └── workflows │ ├── build.yml │ ├── close_stale.yml │ ├── codeql-analysis.yml │ ├── release.yml │ └── trigger-release.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .travis_after_success.sh ├── LICENSE ├── README.md ├── maven_deploy_settings.xml ├── mvnw ├── mvnw.cmd ├── pom.xml ├── renovate.json ├── spotbugs.xml └── src ├── main ├── java │ └── io │ │ └── dropwizard │ │ └── jersey │ │ └── protobuf │ │ ├── Converters.java │ │ ├── InvalidProtocolBufferExceptionMapper.java │ │ ├── ProtobufBundle.java │ │ ├── ProtocolBufferMediaType.java │ │ └── ProtocolBufferMessageBodyProvider.java └── proto │ └── dropwizard.proto └── test ├── java └── io │ └── dropwizard │ └── jersey │ └── protobuf │ ├── ConvertersTest.java │ ├── InvalidProtocolBufferExceptionMapperTest.java │ └── ProtocolBufferMessageBodyProviderTest.java └── proto └── dropwizard.proto /.editorconfig: -------------------------------------------------------------------------------- 1 | # Configuration file for EditorConfig: http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 4 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.properties] 13 | charset = latin1 14 | 15 | [travis.yml] 16 | indent_size = 2 17 | indent_style = space 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.github/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | jcenter 23 | JCenter 24 | https://jcenter.bintray.com 25 | central 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build 3 | # yamllint disable-line rule:truthy 4 | on: 5 | push: 6 | branches: 7 | - release/* 8 | pull_request: 9 | jobs: 10 | yamllint: 11 | uses: dropwizard/workflows/.github/workflows/yamllint.yml@main 12 | build: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | java-version: ['11', '17', '21'] 17 | uses: dropwizard/workflows/.github/workflows/maven.yml@main 18 | secrets: inherit 19 | with: 20 | java-version: ${{ matrix.java-version }} 21 | -------------------------------------------------------------------------------- /.github/workflows/close_stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Close stale issues" 3 | # yamllint disable-line rule:truthy 4 | on: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | # yamllint disable rule:line-length 15 | stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove the "stale" label or comment or this will be closed in 14 days.' 16 | stale-pr-message: 'This pull request is stale because it has been open 90 days with no activity. Remove the "stale" label or comment or this will be closed in 14 days.' 17 | # yamllint enable rule:line-length 18 | days-before-stale: 90 19 | days-before-close: 14 20 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "CodeQL" 3 | # yamllint disable-line rule:truthy 4 | on: 5 | push: 6 | branches: 7 | - 2.1.x 8 | - 3.0.x 9 | - 4.0.x 10 | pull_request: 11 | # The branches below must be a subset of the branches above 12 | branches: 13 | - 2.1.x 14 | - 3.0.x 15 | - 4.0.x 16 | paths: 17 | - "**.java" 18 | - "pom.xml" 19 | schedule: 20 | - cron: '0 21 * * 1' 21 | 22 | jobs: 23 | CodeQL-Build: 24 | # yamllint disable rule:line-length 25 | # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 31 | 32 | # Initializes the CodeQL tools for scanning. 33 | - name: Initialize CodeQL 34 | uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3 35 | # Override language selection by uncommenting this and choosing your languages 36 | # with: 37 | # languages: go, javascript, csharp, python, cpp, java 38 | 39 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 40 | # If this step fails, then you should remove it and run the build manually (see below). 41 | - name: Autobuild 42 | uses: github/codeql-action/autobuild@fca7ace96b7d713c7035871441bd52efbe39e27e # v3 43 | 44 | # ℹ️ Command-line programs to run using the OS shell. 45 | # 📚 https://git.io/JvXDl 46 | 47 | # ✏️ If the Autobuild fails above, remove it and uncomment the following 48 | # three lines and modify them (or add more) to build your code if your 49 | # project uses a compiled language 50 | 51 | # - run: | 52 | # make bootstrap 53 | # make release 54 | 55 | - name: Perform CodeQL Analysis 56 | uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3 57 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | # yamllint disable-line rule:truthy 4 | on: 5 | push: 6 | tags: 7 | - v* 8 | jobs: 9 | release: 10 | uses: dropwizard/workflows/.github/workflows/release.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.github/workflows/trigger-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:comments rule:line-length 3 | name: Trigger Release 4 | # yamllint disable-line rule:truthy 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | releaseVersion: 9 | description: Version of the next release 10 | required: true 11 | type: string 12 | developmentVersion: 13 | description: Version of the next development cycle (must end in "-SNAPSHOT") 14 | required: true 15 | type: string 16 | jobs: 17 | release: 18 | uses: dropwizard/workflows/.github/workflows/trigger-release.yml@main 19 | secrets: inherit 20 | with: 21 | releaseVersion: ${{ inputs.releaseVersion }} 22 | developmentVersion: ${{ inputs.developmentVersion }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .classpath 3 | .project 4 | .settings 5 | target 6 | release.properties 7 | .apt_generated_tests 8 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | import java.net.*; 21 | import java.io.*; 22 | import java.nio.channels.*; 23 | import java.util.Properties; 24 | 25 | public class MavenWrapperDownloader { 26 | 27 | /** 28 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 29 | */ 30 | private static final String DEFAULT_DOWNLOAD_URL = 31 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; 32 | 33 | /** 34 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 35 | * use instead of the default one. 36 | */ 37 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 38 | ".mvn/wrapper/maven-wrapper.properties"; 39 | 40 | /** 41 | * Path where the maven-wrapper.jar will be saved to. 42 | */ 43 | private static final String MAVEN_WRAPPER_JAR_PATH = 44 | ".mvn/wrapper/maven-wrapper.jar"; 45 | 46 | /** 47 | * Name of the property which should be used to override the default download url for the wrapper. 48 | */ 49 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 50 | 51 | public static void main(String args[]) { 52 | System.out.println("- Downloader started"); 53 | File baseDirectory = new File(args[0]); 54 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 55 | 56 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 57 | // wrapperUrl parameter. 58 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 59 | String url = DEFAULT_DOWNLOAD_URL; 60 | if(mavenWrapperPropertyFile.exists()) { 61 | FileInputStream mavenWrapperPropertyFileInputStream = null; 62 | try { 63 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 64 | Properties mavenWrapperProperties = new Properties(); 65 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 66 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 67 | } catch (IOException e) { 68 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 69 | } finally { 70 | try { 71 | if(mavenWrapperPropertyFileInputStream != null) { 72 | mavenWrapperPropertyFileInputStream.close(); 73 | } 74 | } catch (IOException e) { 75 | // Ignore ... 76 | } 77 | } 78 | } 79 | System.out.println("- Downloading from: : " + url); 80 | 81 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 82 | if(!outputFile.getParentFile().exists()) { 83 | if(!outputFile.getParentFile().mkdirs()) { 84 | System.out.println( 85 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 86 | } 87 | } 88 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 89 | try { 90 | downloadFileFromURL(url, outputFile); 91 | System.out.println("Done"); 92 | System.exit(0); 93 | } catch (Throwable e) { 94 | System.out.println("- Error downloading"); 95 | e.printStackTrace(); 96 | System.exit(1); 97 | } 98 | } 99 | 100 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 101 | URL website = new URL(urlString); 102 | ReadableByteChannel rbc; 103 | rbc = Channels.newChannel(website.openStream()); 104 | FileOutputStream fos = new FileOutputStream(destination); 105 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 106 | fos.close(); 107 | rbc.close(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropwizard/dropwizard-protobuf/eee9dcb0cd2c8e71cf545101665f3e957c8bd8c3/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.10/apache-maven-3.9.10-bin.zip -------------------------------------------------------------------------------- /.travis_after_success.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "${TRAVIS_JDK_VERSION}" != "openjdk11" ]]; then 4 | echo "Skipping after_success actions for JDK version \"${TRAVIS_JDK_VERSION}\"" 5 | exit 6 | fi 7 | 8 | if [[ -n ${TRAVIS_TAG} ]]; then 9 | echo "Skipping deployment for tag \"${TRAVIS_TAG}\"" 10 | exit 11 | fi 12 | 13 | if [[ ${TRAVIS_BRANCH} != 'master' ]]; then 14 | echo "Skipping deployment for branch \"${TRAVIS_BRANCH}\"" 15 | exit 16 | fi 17 | 18 | if [[ "$TRAVIS_PULL_REQUEST" = "true" ]]; then 19 | echo "Skipping deployment for pull request" 20 | exit 21 | fi 22 | 23 | ./mvnw -B deploy --settings maven_deploy_settings.xml -Dmaven.test.skip=true 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dropwizard Protobuf 2 | =================== 3 | [![Build Status](https://travis-ci.org/dropwizard/dropwizard-protobuf.svg?branch=master)](https://travis-ci.org/dropwizard/dropwizard-protobuf) 4 | [![Maven Central](https://img.shields.io/maven-central/v/io.dropwizard.modules/dropwizard-protobuf.svg?style=flat-square)](https://maven-badges.herokuapp.com/maven-central/io.dropwizard.modules/dropwizard-protobuf/) 5 | [![GitHub license](https://img.shields.io/github/license/dropwizard/dropwizard-protobuf.svg?style=flat-square)](https://github.com/dropwizard/dropwizard-protobuf/tree/master) 6 | 7 | 8 | `dropwizard-protobuf` is a [Jersey](https://eclipse-ee4j.github.io/jersey/) [JAX-RS Entity Provider](https://www.oracle.com/technical-resources/articles/java/jax-rs.html) that allows reading and writing messages in Google's [Protocol Buffers](https://developers.google.com/protocol-buffers/) format. 9 | 10 | 11 | Usage 12 | ----- 13 | 14 | Just add the `ProtocolBundle` to your Dropwizard application inside the [`Application#initialize`](https://javadoc.io/static/io.dropwizard/dropwizard-project/2.0.7/io/dropwizard/Application.html#initialize-io.dropwizard.setup.Bootstrap-) method. 15 | 16 | ```java 17 | @Override 18 | public void initialize(Bootstrap bootstrap) { 19 | bootstrap.addBundle(new ProtobufBundle()); 20 | } 21 | ``` 22 | 23 | Maven Artifacts 24 | --------------- 25 | 26 | This project is available on Maven Central. To add it to your project simply add the following dependencies to your `pom.xml`: 27 | 28 | ```xml 29 | 30 | io.dropwizard.modules 31 | dropwizard-protobuf 32 | 2.0.7-1 33 | 34 | ``` 35 | 36 | Support 37 | ------- 38 | 39 | Please file bug reports and feature requests in [GitHub issues](https://github.com/dropwizard/dropwizard-protobuf/issues). 40 | 41 | 42 | License 43 | ------- 44 | 45 | Copyright (c) 2020 Smoke Turner, LLC 46 | 47 | This library is licensed under the Apache License, Version 2.0. 48 | 49 | See http://www.apache.org/licenses/LICENSE-2.0.html or the LICENSE file in this repository for the full license text. 50 | -------------------------------------------------------------------------------- /maven_deploy_settings.xml: -------------------------------------------------------------------------------- 1 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | sonatype-nexus-snapshots 30 | ${env.CI_DEPLOY_USERNAME} 31 | ${env.CI_DEPLOY_PASSWORD} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 233 | ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS 234 | 235 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% 146 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 4.0.0 21 | 22 | io.dropwizard.modules 23 | module-parent 24 | 4.0.4 25 | 26 | 27 | 28 | io.dropwizard.modules 29 | dropwizard-protobuf 30 | 4.0.7-SNAPSHOT 31 | jar 32 | 33 | Dropwizard Protocol Buffers Support 34 | Support for reading and writing Google Protocol Buffer objects 35 | 36 | 37 | 4.31.1 38 | dropwizard_dropwizard-protobuf 39 | 40 | 41 | 42 | 43 | 44 | com.google.protobuf 45 | protobuf-bom 46 | ${protobuf.version} 47 | pom 48 | import 49 | 50 | 51 | 52 | 53 | 54 | 55 | io.dropwizard 56 | dropwizard-core 57 | 58 | 59 | com.google.protobuf 60 | protobuf-java 61 | 62 | 63 | com.google.protobuf 64 | protobuf-java-util 65 | 66 | 67 | org.junit.jupiter 68 | junit-jupiter 69 | test 70 | 71 | 72 | org.assertj 73 | assertj-core 74 | test 75 | 76 | 77 | 78 | 79 | 80 | 81 | kr.motd.maven 82 | os-maven-plugin 83 | 1.7.1 84 | 85 | 86 | 87 | 88 | 89 | org.xolstice.maven.plugins 90 | protobuf-maven-plugin 91 | 0.6.1 92 | true 93 | 94 | com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} 95 | 96 | 97 | 98 | 99 | compile 100 | test-compile 101 | 102 | 103 | 104 | 105 | 106 | com.spotify.fmt 107 | fmt-maven-plugin 108 | 2.25 109 | 110 | 111 | 112 | format 113 | 114 | process-sources 115 | 116 | 117 | 118 | 119 | com.google.googlejavaformat 120 | google-java-format 121 | 1.24.0 122 | 123 | 124 | 125 | 126 | org.apache.maven.plugins 127 | maven-release-plugin 128 | 129 | v@{project.version} 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>dropwizard/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /spotbugs.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/jersey/protobuf/Converters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import com.google.common.base.Converter; 19 | import com.google.protobuf.Timestamp; 20 | import com.google.protobuf.util.Durations; 21 | import com.google.protobuf.util.Timestamps; 22 | import java.text.ParseException; 23 | import java.time.Duration; 24 | import java.time.Instant; 25 | import java.time.OffsetDateTime; 26 | import java.time.ZoneOffset; 27 | import java.time.ZonedDateTime; 28 | 29 | public class Converters { 30 | 31 | public static final Converter toStringUTC = 32 | Converter.from( 33 | Timestamps::toString, 34 | s -> { 35 | try { 36 | return Timestamps.parse(s); 37 | } catch (ParseException e) { 38 | return null; 39 | } 40 | }); 41 | 42 | public static final Converter toInstantUTC = 43 | Converter.from( 44 | t -> Instant.ofEpochSecond(t.getSeconds(), t.getNanos()), 45 | i -> Timestamp.newBuilder().setSeconds(i.getEpochSecond()).setNanos(i.getNano()).build()); 46 | 47 | public static final Converter toOffsetDateTimeUTC = 48 | Converter.from( 49 | t -> toInstantUTC.convert(t).atOffset(ZoneOffset.UTC), 50 | o -> toInstantUTC.reverse().convert(o.toInstant())); 51 | 52 | public static final Converter toZonedDateTimeUTC = 53 | Converter.from( 54 | t -> toOffsetDateTimeUTC.convert(t).toZonedDateTime(), 55 | z -> toOffsetDateTimeUTC.reverse().convert(z.toOffsetDateTime())); 56 | 57 | public static final Converter toDuration = 58 | Converter.from( 59 | d -> Duration.ofSeconds(d.getSeconds(), d.getNanos()), 60 | d -> 61 | com.google.protobuf.Duration.newBuilder() 62 | .setSeconds(d.getSeconds()) 63 | .setNanos(d.getNano()) 64 | .build()); 65 | 66 | public static final Converter toDurationString = 67 | Converter.from( 68 | Durations::toString, 69 | s -> { 70 | try { 71 | return Durations.parse(s); 72 | } catch (ParseException e) { 73 | return null; 74 | } 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/jersey/protobuf/InvalidProtocolBufferExceptionMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import com.google.protobuf.InvalidProtocolBufferException; 19 | import io.dropwizard.jersey.protobuf.protos.DropwizardProtos.ErrorMessage; 20 | import jakarta.ws.rs.core.Response; 21 | import jakarta.ws.rs.ext.ExceptionMapper; 22 | import jakarta.ws.rs.ext.Provider; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | @Provider 27 | public class InvalidProtocolBufferExceptionMapper 28 | implements ExceptionMapper { 29 | private static final Logger LOGGER = 30 | LoggerFactory.getLogger(InvalidProtocolBufferExceptionMapper.class); 31 | 32 | @Override 33 | public Response toResponse(InvalidProtocolBufferException exception) { 34 | final ErrorMessage message = 35 | ErrorMessage.newBuilder() 36 | .setMessage("Unable to process protocol buffer") 37 | .setCode(Response.Status.BAD_REQUEST.getStatusCode()) 38 | .build(); 39 | 40 | LOGGER.debug("Unable to process protocol buffer message", exception); 41 | return Response.status(Response.Status.BAD_REQUEST) 42 | .type(ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE) 43 | .entity(message) 44 | .build(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/jersey/protobuf/ProtobufBundle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import io.dropwizard.core.Configuration; 19 | import io.dropwizard.core.ConfiguredBundle; 20 | import io.dropwizard.core.setup.Bootstrap; 21 | import io.dropwizard.core.setup.Environment; 22 | 23 | public class ProtobufBundle implements ConfiguredBundle { 24 | 25 | @Override 26 | public void initialize(Bootstrap bootstrap) { 27 | // nothing to initialize 28 | } 29 | 30 | @Override 31 | public void run(C configuration, Environment environment) { 32 | environment.jersey().register(ProtocolBufferMessageBodyProvider.class); 33 | environment.jersey().register(InvalidProtocolBufferExceptionMapper.class); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/jersey/protobuf/ProtocolBufferMediaType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import jakarta.ws.rs.core.MediaType; 19 | 20 | public class ProtocolBufferMediaType extends MediaType { 21 | 22 | /** "application/x-protobuf" */ 23 | public static final String APPLICATION_PROTOBUF = "application/x-protobuf"; 24 | 25 | /** "application/x-protobuf" */ 26 | public static final MediaType APPLICATION_PROTOBUF_TYPE = 27 | new MediaType("application", "x-protobuf"); 28 | 29 | /** "application/x-protobuf-text-format" */ 30 | public static final String APPLICATION_PROTOBUF_TEXT = "application/x-protobuf-text-format"; 31 | 32 | /** "application/x-protobuf-text-format" */ 33 | public static final MediaType APPLICATION_PROTOBUF_TEXT_TYPE = 34 | new MediaType("application", "x-protobuf-text-format"); 35 | 36 | /** "application/x-protobuf-json-format" */ 37 | public static final String APPLICATION_PROTOBUF_JSON = "application/x-protobuf-json-format"; 38 | 39 | /** "application/x-protobuf-json-format" */ 40 | public static final MediaType APPLICATION_PROTOBUF_JSON_TYPE = 41 | new MediaType("application", "x-protobuf-json-format"); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/jersey/protobuf/ProtocolBufferMessageBodyProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import com.google.protobuf.InvalidProtocolBufferException; 19 | import com.google.protobuf.Message; 20 | import com.google.protobuf.TextFormat; 21 | import com.google.protobuf.util.JsonFormat; 22 | import jakarta.ws.rs.Consumes; 23 | import jakarta.ws.rs.Produces; 24 | import jakarta.ws.rs.WebApplicationException; 25 | import jakarta.ws.rs.core.MediaType; 26 | import jakarta.ws.rs.core.MultivaluedMap; 27 | import jakarta.ws.rs.ext.MessageBodyReader; 28 | import jakarta.ws.rs.ext.MessageBodyWriter; 29 | import jakarta.ws.rs.ext.Provider; 30 | import java.io.IOException; 31 | import java.io.InputStream; 32 | import java.io.InputStreamReader; 33 | import java.io.OutputStream; 34 | import java.lang.annotation.Annotation; 35 | import java.lang.reflect.Method; 36 | import java.lang.reflect.Type; 37 | import java.nio.charset.StandardCharsets; 38 | import java.util.Map; 39 | import java.util.concurrent.ConcurrentHashMap; 40 | 41 | /** 42 | * A Jersey provider which enables using Protocol Buffers to parse request entities into objects and 43 | * generate response entities from objects. 44 | */ 45 | @Provider 46 | @Consumes({ 47 | ProtocolBufferMediaType.APPLICATION_PROTOBUF, 48 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TEXT, 49 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_JSON 50 | }) 51 | @Produces({ 52 | ProtocolBufferMediaType.APPLICATION_PROTOBUF, 53 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TEXT, 54 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_JSON 55 | }) 56 | public class ProtocolBufferMessageBodyProvider 57 | implements MessageBodyReader, MessageBodyWriter { 58 | 59 | private final Map, Method> methodCache = new ConcurrentHashMap<>(); 60 | 61 | @Override 62 | public boolean isReadable( 63 | final Class type, 64 | final Type genericType, 65 | final Annotation[] annotations, 66 | final MediaType mediaType) { 67 | return Message.class.isAssignableFrom(type); 68 | } 69 | 70 | @Override 71 | public Message readFrom( 72 | final Class type, 73 | final Type genericType, 74 | final Annotation[] annotations, 75 | final MediaType mediaType, 76 | final MultivaluedMap httpHeaders, 77 | final InputStream entityStream) 78 | throws IOException { 79 | 80 | final Method newBuilder = 81 | methodCache.computeIfAbsent( 82 | type, 83 | t -> { 84 | try { 85 | return t.getMethod("newBuilder"); 86 | } catch (Exception e) { 87 | return null; 88 | } 89 | }); 90 | 91 | final Message.Builder builder; 92 | try { 93 | builder = (Message.Builder) newBuilder.invoke(type); 94 | } catch (Exception e) { 95 | throw new WebApplicationException(e); 96 | } 97 | 98 | if (mediaType.getSubtype().contains("text-format")) { 99 | TextFormat.merge(new InputStreamReader(entityStream, StandardCharsets.UTF_8), builder); 100 | return builder.build(); 101 | } else if (mediaType.getSubtype().contains("json-format")) { 102 | JsonFormat.parser() 103 | .ignoringUnknownFields() 104 | .merge(new InputStreamReader(entityStream, StandardCharsets.UTF_8), builder); 105 | return builder.build(); 106 | } else { 107 | return builder.mergeFrom(entityStream).build(); 108 | } 109 | } 110 | 111 | @Override 112 | public long getSize( 113 | final Message m, 114 | final Class type, 115 | final Type genericType, 116 | final Annotation[] annotations, 117 | final MediaType mediaType) { 118 | 119 | if (mediaType.getSubtype().contains("text-format")) { 120 | final String formatted = m.toString(); 121 | return formatted.getBytes(StandardCharsets.UTF_8).length; 122 | } else if (mediaType.getSubtype().contains("json-format")) { 123 | try { 124 | final String formatted = JsonFormat.printer().omittingInsignificantWhitespace().print(m); 125 | return formatted.getBytes(StandardCharsets.UTF_8).length; 126 | } catch (InvalidProtocolBufferException e) { 127 | // invalid protocol message 128 | return -1L; 129 | } 130 | } 131 | 132 | return m.getSerializedSize(); 133 | } 134 | 135 | @Override 136 | public boolean isWriteable( 137 | final Class type, 138 | final Type genericType, 139 | final Annotation[] annotations, 140 | final MediaType mediaType) { 141 | return Message.class.isAssignableFrom(type); 142 | } 143 | 144 | @Override 145 | public void writeTo( 146 | final Message m, 147 | final Class type, 148 | final Type genericType, 149 | final Annotation[] annotations, 150 | final MediaType mediaType, 151 | final MultivaluedMap httpHeaders, 152 | final OutputStream entityStream) 153 | throws IOException { 154 | 155 | if (mediaType.getSubtype().contains("text-format")) { 156 | entityStream.write(m.toString().getBytes(StandardCharsets.UTF_8)); 157 | } else if (mediaType.getSubtype().contains("json-format")) { 158 | final String formatted = JsonFormat.printer().omittingInsignificantWhitespace().print(m); 159 | entityStream.write(formatted.getBytes(StandardCharsets.UTF_8)); 160 | } else { 161 | m.writeTo(entityStream); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/proto/dropwizard.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package dropwizard; 4 | 5 | option java_package = "io.dropwizard.jersey.protobuf.protos"; 6 | option java_outer_classname = "DropwizardProtos"; 7 | option optimize_for = SPEED; 8 | 9 | message ErrorMessage { 10 | required string message = 1; 11 | optional int32 code = 2 [default = 500]; 12 | optional string details = 3; 13 | }; 14 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/jersey/protobuf/ConvertersTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import static org.assertj.core.api.Assertions.assertThat; 19 | 20 | import com.google.protobuf.Timestamp; 21 | import java.time.Duration; 22 | import java.time.Instant; 23 | import java.time.OffsetDateTime; 24 | import java.time.ZonedDateTime; 25 | import org.junit.jupiter.api.Test; 26 | 27 | public class ConvertersTest { 28 | 29 | @Test 30 | public void testToString() { 31 | final Timestamp timestamp = 32 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 33 | 34 | final String actual = Converters.toStringUTC.convert(timestamp); 35 | final String expected = "2018-01-12T12:45:32.123Z"; 36 | 37 | assertThat(actual).isEqualTo(expected); 38 | } 39 | 40 | @Test 41 | public void testFromString() { 42 | final String instant = "2018-01-12T12:45:32.123Z"; 43 | 44 | final Timestamp actual = Converters.toStringUTC.reverse().convert(instant); 45 | final Timestamp expected = 46 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 47 | 48 | assertThat(actual).isEqualTo(expected); 49 | } 50 | 51 | @Test 52 | public void testToInstant() { 53 | final Timestamp timestamp = 54 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 55 | 56 | final Instant actual = Converters.toInstantUTC.convert(timestamp); 57 | final Instant expected = Instant.parse("2018-01-12T12:45:32.123Z"); 58 | 59 | assertThat(actual).isEqualTo(expected); 60 | } 61 | 62 | @Test 63 | public void testFromInstant() { 64 | final Instant instant = Instant.parse("2018-01-12T12:45:32.123Z"); 65 | 66 | final Timestamp actual = Converters.toInstantUTC.reverse().convert(instant); 67 | final Timestamp expected = 68 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 69 | 70 | assertThat(actual).isEqualTo(expected); 71 | } 72 | 73 | @Test 74 | public void testToOffsetDateTime() { 75 | final Timestamp timestamp = 76 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 77 | 78 | final OffsetDateTime actual = Converters.toOffsetDateTimeUTC.convert(timestamp); 79 | final OffsetDateTime expected = OffsetDateTime.parse("2018-01-12T12:45:32.123Z"); 80 | 81 | assertThat(actual).isEqualTo(expected); 82 | } 83 | 84 | @Test 85 | public void testFromOffsetDateTime() { 86 | final OffsetDateTime offset = OffsetDateTime.parse("2018-01-12T12:45:32.123Z"); 87 | 88 | final Timestamp actual = Converters.toOffsetDateTimeUTC.reverse().convert(offset); 89 | final Timestamp expected = 90 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 91 | 92 | assertThat(actual).isEqualTo(expected); 93 | } 94 | 95 | @Test 96 | public void testToZonedDateTime() { 97 | final Timestamp timestamp = 98 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 99 | 100 | final ZonedDateTime actual = Converters.toZonedDateTimeUTC.convert(timestamp); 101 | final ZonedDateTime expected = ZonedDateTime.parse("2018-01-12T12:45:32.123Z"); 102 | 103 | assertThat(actual).isEqualTo(expected); 104 | } 105 | 106 | @Test 107 | public void testFromZonedDateTime() { 108 | final ZonedDateTime offset = ZonedDateTime.parse("2018-01-12T12:45:32.123Z"); 109 | 110 | final Timestamp actual = Converters.toZonedDateTimeUTC.reverse().convert(offset); 111 | final Timestamp expected = 112 | Timestamp.newBuilder().setSeconds(1515761132).setNanos(123000000).build(); 113 | 114 | assertThat(actual).isEqualTo(expected); 115 | } 116 | 117 | @Test 118 | public void testToDuration() { 119 | final com.google.protobuf.Duration duration = 120 | com.google.protobuf.Duration.newBuilder().setSeconds(20).setNanos(345_000_000).build(); 121 | 122 | final Duration actual = Converters.toDuration.convert(duration); 123 | final Duration expected = Duration.parse("PT20.345S"); 124 | 125 | assertThat(actual).isEqualTo(expected); 126 | } 127 | 128 | @Test 129 | public void testFromDuration() { 130 | final Duration duration = Duration.parse("PT20.345S"); 131 | 132 | final com.google.protobuf.Duration actual = Converters.toDuration.reverse().convert(duration); 133 | final com.google.protobuf.Duration expected = 134 | com.google.protobuf.Duration.newBuilder().setSeconds(20).setNanos(345_000_000).build(); 135 | 136 | assertThat(actual).isEqualTo(expected); 137 | } 138 | 139 | @Test 140 | public void testToDurationString() { 141 | final com.google.protobuf.Duration duration = 142 | com.google.protobuf.Duration.newBuilder().setSeconds(20).setNanos(345_000_000).build(); 143 | 144 | final String actual = Converters.toDurationString.convert(duration); 145 | final String expected = "20.345s"; 146 | 147 | assertThat(actual).isEqualTo(expected); 148 | } 149 | 150 | @Test 151 | public void testFromDurationString() { 152 | final String duration = "20.345s"; 153 | 154 | final com.google.protobuf.Duration actual = 155 | Converters.toDurationString.reverse().convert(duration); 156 | final com.google.protobuf.Duration expected = 157 | com.google.protobuf.Duration.newBuilder().setSeconds(20).setNanos(345_000_000).build(); 158 | 159 | assertThat(actual).isEqualTo(expected); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/jersey/protobuf/InvalidProtocolBufferExceptionMapperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import static org.assertj.core.api.Assertions.assertThat; 19 | 20 | import com.google.protobuf.InvalidProtocolBufferException; 21 | import jakarta.ws.rs.core.Response; 22 | import jakarta.ws.rs.core.Response.Status; 23 | import org.junit.jupiter.api.Test; 24 | 25 | public class InvalidProtocolBufferExceptionMapperTest { 26 | 27 | @Test 28 | public void testExceptionMapperReturnsBadRequestResponseWhenInvalidProtocolBufferException() { 29 | final InvalidProtocolBufferExceptionMapper mapper = new InvalidProtocolBufferExceptionMapper(); 30 | final Response actual = 31 | mapper.toResponse(new InvalidProtocolBufferException("Something went wrong")); 32 | 33 | assertThat(actual.getStatus()).isEqualTo(Status.BAD_REQUEST.getStatusCode()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/jersey/protobuf/ProtocolBufferMessageBodyProviderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Smoke Turner, LLC (github@smoketurner.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.dropwizard.jersey.protobuf; 17 | 18 | import static org.assertj.core.api.Assertions.assertThat; 19 | import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; 20 | 21 | import com.google.protobuf.InvalidProtocolBufferException; 22 | import com.google.protobuf.Message; 23 | import io.dropwizard.jersey.protobuf.protos.DropwizardProtosTest.Example; 24 | import io.dropwizard.jersey.protobuf.protos.DropwizardProtosTest.Example2; 25 | import io.dropwizard.jersey.protobuf.protos.DropwizardProtosTest.ExampleNewVersion; 26 | import jakarta.ws.rs.core.MultivaluedHashMap; 27 | import java.io.ByteArrayInputStream; 28 | import java.io.ByteArrayOutputStream; 29 | import java.io.IOException; 30 | import java.lang.annotation.Annotation; 31 | import java.nio.charset.StandardCharsets; 32 | import org.glassfish.jersey.internal.util.collection.StringKeyIgnoreCaseMultivaluedMap; 33 | import org.junit.jupiter.api.Test; 34 | 35 | public class ProtocolBufferMessageBodyProviderTest { 36 | private final Annotation[] NONE = new Annotation[0]; 37 | private final ProtocolBufferMessageBodyProvider provider = 38 | new ProtocolBufferMessageBodyProvider(); 39 | private final Example example = Example.newBuilder().setId(1337L).build(); 40 | private final Example2 example2 = Example2.newBuilder().setName("example").build(); 41 | private final ExampleNewVersion exampleNewVersion = 42 | ExampleNewVersion.newBuilder().setId(1337L).setNewValidField("example").build(); 43 | 44 | @Test 45 | public void readsDeserializableTypes() throws Exception { 46 | assertThat(provider.isReadable(Example.class, null, null, null)).isTrue(); 47 | } 48 | 49 | @Test 50 | public void writesSerializableTypes() throws Exception { 51 | assertThat(provider.isWriteable(Example.class, null, null, null)).isTrue(); 52 | } 53 | 54 | @Test 55 | public void deserializesRequestEntities() throws Exception { 56 | final Object actualExample1 = 57 | readFrom(provider, Example.class, new ByteArrayInputStream(example.toByteArray())); 58 | assertThat(actualExample1).isInstanceOf(Example.class); 59 | assertThat(((Example) actualExample1).getId()).isEqualTo(1337L); 60 | 61 | final Object actualExample2 = 62 | readFrom(provider, Example.class, new ByteArrayInputStream(example.toByteArray())); 63 | assertThat(actualExample2).isInstanceOf(Example.class); 64 | assertThat(((Example) actualExample2).getId()).isEqualTo(1337L); 65 | 66 | final Object actualExample3 = 67 | readFrom(provider, Example2.class, new ByteArrayInputStream(example2.toByteArray())); 68 | assertThat(actualExample3).isInstanceOf(Example2.class); 69 | assertThat(((Example2) actualExample3).getName()).isEqualTo("example"); 70 | 71 | final Object actualExample4 = 72 | readFrom( 73 | provider, 74 | ExampleNewVersion.class, 75 | new ByteArrayInputStream(exampleNewVersion.toByteArray())); 76 | assertThat(actualExample4).isInstanceOf(ExampleNewVersion.class); 77 | assertThat(((ExampleNewVersion) actualExample4).getId()).isEqualTo(1337L); 78 | assertThat(((ExampleNewVersion) actualExample4).getNewValidField()).isEqualTo("example"); 79 | 80 | final Object actualExample5 = 81 | readFrom( 82 | provider, Example.class, new ByteArrayInputStream(exampleNewVersion.toByteArray())); 83 | assertThat(actualExample5).isInstanceOf(Example.class); 84 | assertThat(((Example) actualExample5).getId()).isEqualTo(1337L); 85 | } 86 | 87 | private Object readFrom( 88 | ProtocolBufferMessageBodyProvider provider, Class clazz, ByteArrayInputStream entity) 89 | throws IOException { 90 | final Object obj = 91 | provider.readFrom( 92 | (Class) clazz, 93 | clazz, 94 | NONE, 95 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE, 96 | new MultivaluedHashMap(), 97 | entity); 98 | return obj; 99 | } 100 | 101 | @Test 102 | public void deserializesRequestTextEntities() throws Exception { 103 | final Object actualExample1 = 104 | readStringFrom( 105 | provider, 106 | Example.class, 107 | new ByteArrayInputStream(example.toString().getBytes(StandardCharsets.UTF_8))); 108 | assertThat(actualExample1).isInstanceOf(Example.class); 109 | assertThat(((Example) actualExample1).getId()).isEqualTo(1337L); 110 | 111 | final Object actualExample2 = 112 | readFrom(provider, Example2.class, new ByteArrayInputStream(example2.toByteArray())); 113 | assertThat(actualExample2).isInstanceOf(Example2.class); 114 | assertThat(((Example2) actualExample2).getName()).isEqualTo("example"); 115 | } 116 | 117 | private Object readStringFrom( 118 | ProtocolBufferMessageBodyProvider provider, Class clazz, ByteArrayInputStream entity) 119 | throws IOException { 120 | final Object obj = 121 | provider.readFrom( 122 | (Class) clazz, 123 | clazz, 124 | NONE, 125 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TEXT_TYPE, 126 | new MultivaluedHashMap(), 127 | entity); 128 | return obj; 129 | } 130 | 131 | @Test 132 | public void deserializesRequestJsonEntities() throws Exception { 133 | final Object actualExample1 = 134 | readJsonFrom( 135 | provider, 136 | Example.class, 137 | new ByteArrayInputStream("{\"id\":\"1337\"}".getBytes(StandardCharsets.UTF_8))); 138 | assertThat(actualExample1).isInstanceOf(Example.class); 139 | assertThat(((Example) actualExample1).getId()).isEqualTo(1337L); 140 | 141 | final Object actualExample2 = 142 | readJsonFrom( 143 | provider, 144 | Example2.class, 145 | new ByteArrayInputStream("{\"name\":\"example\"}".getBytes(StandardCharsets.UTF_8))); 146 | assertThat(actualExample2).isInstanceOf(Example2.class); 147 | assertThat(((Example2) actualExample2).getName()).isEqualTo("example"); 148 | 149 | final Object actualExample3 = 150 | readJsonFrom( 151 | provider, 152 | ExampleNewVersion.class, 153 | new ByteArrayInputStream( 154 | "{\"id\":\"1337\", \"newValidField\":\"example\"}" 155 | .getBytes(StandardCharsets.UTF_8))); 156 | assertThat(actualExample3).isInstanceOf(ExampleNewVersion.class); 157 | assertThat(((ExampleNewVersion) actualExample3).getId()).isEqualTo(1337L); 158 | assertThat(((ExampleNewVersion) actualExample3).getNewValidField()).isEqualTo("example"); 159 | 160 | final Object actualExample4 = 161 | readJsonFrom( 162 | provider, 163 | Example.class, 164 | new ByteArrayInputStream( 165 | "{\"id\":\"1337\", \"newValidField\":\"example\"}" 166 | .getBytes(StandardCharsets.UTF_8))); 167 | assertThat(actualExample4).isInstanceOf(Example.class); 168 | assertThat(((Example) actualExample4).getId()).isEqualTo(1337L); 169 | } 170 | 171 | private Object readJsonFrom( 172 | ProtocolBufferMessageBodyProvider provider, Class clazz, ByteArrayInputStream entity) 173 | throws IOException { 174 | final Object obj = 175 | provider.readFrom( 176 | (Class) clazz, 177 | clazz, 178 | NONE, 179 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_JSON_TYPE, 180 | new MultivaluedHashMap(), 181 | entity); 182 | return obj; 183 | } 184 | 185 | @Test 186 | public void throwsInvalidProtocolBufferExceptionForMalformedRequestEntities() throws Exception { 187 | final ByteArrayInputStream entity = 188 | new ByteArrayInputStream("{\"id\":-1d".getBytes(StandardCharsets.UTF_8)); 189 | 190 | try { 191 | final Class klass = Example.class; 192 | provider.readFrom( 193 | (Class) klass, 194 | Example.class, 195 | NONE, 196 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE, 197 | new MultivaluedHashMap(), 198 | entity); 199 | failBecauseExceptionWasNotThrown(InvalidProtocolBufferException.class); 200 | } catch (InvalidProtocolBufferException e) { 201 | } 202 | } 203 | 204 | @Test 205 | public void serializesResponseEntities() throws Exception { 206 | final ByteArrayOutputStream output = new ByteArrayOutputStream(); 207 | 208 | final Example example = Example.newBuilder().setId(1L).build(); 209 | 210 | provider.writeTo( 211 | example, 212 | Example.class, 213 | Example.class, 214 | NONE, 215 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE, 216 | new StringKeyIgnoreCaseMultivaluedMap<>(), 217 | output); 218 | 219 | assertThat(output.toByteArray()).isEqualTo(example.toByteArray()); 220 | } 221 | 222 | @Test 223 | public void serializesResponseTextEntities() throws Exception { 224 | final ByteArrayOutputStream output = new ByteArrayOutputStream(); 225 | 226 | final Example example = Example.newBuilder().setId(1L).build(); 227 | 228 | provider.writeTo( 229 | example, 230 | Example.class, 231 | Example.class, 232 | NONE, 233 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TEXT_TYPE, 234 | new StringKeyIgnoreCaseMultivaluedMap<>(), 235 | output); 236 | 237 | assertThat(output.toString()).isEqualTo("id: 1\n"); 238 | } 239 | 240 | @Test 241 | public void serializesResponseJsonEntities() throws Exception { 242 | final ByteArrayOutputStream output = new ByteArrayOutputStream(); 243 | 244 | final Example example = Example.newBuilder().setId(1L).build(); 245 | 246 | provider.writeTo( 247 | example, 248 | Example.class, 249 | Example.class, 250 | NONE, 251 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_JSON_TYPE, 252 | new StringKeyIgnoreCaseMultivaluedMap<>(), 253 | output); 254 | 255 | assertThat(output.toString()).isEqualTo("{\"id\":\"1\"}"); 256 | } 257 | 258 | @Test 259 | public void responseEntitySize() throws Exception { 260 | final Example example = Example.newBuilder().setId(1L).build(); 261 | 262 | final long size = 263 | provider.getSize( 264 | example, 265 | Example.class, 266 | Example.class, 267 | NONE, 268 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE); 269 | 270 | assertThat(size).isEqualTo(2L); 271 | } 272 | 273 | @Test 274 | public void responseTextEntitySize() throws Exception { 275 | final Example example = Example.newBuilder().setId(1L).build(); 276 | 277 | final long size = 278 | provider.getSize( 279 | example, 280 | Example.class, 281 | Example.class, 282 | NONE, 283 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_TEXT_TYPE); 284 | 285 | assertThat(size).isEqualTo(6L); 286 | } 287 | 288 | @Test 289 | public void responseJsonEntitySize() throws Exception { 290 | final Example example = Example.newBuilder().setId(1L).build(); 291 | 292 | final long size = 293 | provider.getSize( 294 | example, 295 | Example.class, 296 | Example.class, 297 | NONE, 298 | ProtocolBufferMediaType.APPLICATION_PROTOBUF_JSON_TYPE); 299 | 300 | assertThat(size).isEqualTo(10L); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/test/proto/dropwizard.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package dropwizard; 4 | 5 | option java_package = "io.dropwizard.jersey.protobuf.protos"; 6 | option java_outer_classname = "DropwizardProtosTest"; 7 | option optimize_for = SPEED; 8 | 9 | message Example { 10 | int64 id = 1; 11 | }; 12 | 13 | message Example2 { 14 | string name = 1; 15 | }; 16 | 17 | message ExampleNewVersion { 18 | int64 id = 1; 19 | string newValidField = 2; 20 | }; 21 | --------------------------------------------------------------------------------