├── .github └── workflows │ └── it.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml ├── release.sh ├── subzero-all-it-hz310 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz310 │ └── Hazelcast310_SmokeTest.java ├── subzero-all-it-hz311 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz311 │ └── Hazelcast311_SmokeTest.java ├── subzero-all-it-hz312 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz312 │ └── Hazelcast312_SmokeTest.java ├── subzero-all-it-hz36 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz36 │ └── Hazelcast36_SmokeTest.java ├── subzero-all-it-hz37 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz37 │ └── Hazelcast37_SmokeTest.java ├── subzero-all-it-hz38 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz38 │ └── Hazelcast38_SmokeTest.java ├── subzero-all-it-hz39 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz39 │ └── Hazelcast39_SmokeTest.java ├── subzero-all-it-hz40 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz40 │ └── Hazelcast40_SmokeTest.java ├── subzero-all-it-hz50 ├── pom.xml └── src │ └── test │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── it │ └── hz50 │ └── Hazelcast50_SmokeTest.java ├── subzero-all ├── pom.xml └── src │ └── main │ └── java │ └── info │ └── jerrinot │ └── subzero │ └── Placeholder.java ├── subzero-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── info │ │ └── jerrinot │ │ └── subzero │ │ ├── AbstractGlobalUserSerializer.java │ │ ├── AbstractSerializer.java │ │ ├── AbstractTypeSpecificUserSerializer.java │ │ ├── ClassFactory.java │ │ ├── KryoConfigurer.java │ │ ├── Serializer.java │ │ ├── SerializerConfigurer.java │ │ ├── SubZero.java │ │ ├── UserSerializer.java │ │ ├── UserSerializerConfig.java │ │ ├── configurers │ │ └── EnableSyntheticFields.java │ │ ├── example │ │ └── HashMapSerializerExample.java │ │ └── internal │ │ ├── ClassLoaderUtils.java │ │ ├── IdGeneratorUtils.java │ │ ├── PostProcessingSerializerFactory.java │ │ ├── PropertyUserSerializer.java │ │ ├── WellKnownClassesRepository.java │ │ └── strategy │ │ ├── DelegatingClassResolver.java │ │ ├── GlobalKryoStrategy.java │ │ ├── KryoContext.java │ │ ├── KryoStrategy.java │ │ └── TypedKryoStrategy.java │ └── test │ ├── java │ └── info │ │ └── jerrinot │ │ └── subzero │ │ ├── ArraysAsListClassFactory.java │ │ ├── ClassLoadingTest.java │ │ ├── SerializerTest.java │ │ ├── SubZeroTest.java │ │ ├── SubzeroConfigRule.java │ │ ├── TestCustomerSerializers.java │ │ ├── example │ │ └── HashMapSerializerExampleTest.java │ │ ├── internal │ │ └── strategy │ │ │ ├── GlobalKryoStrategyTest.java │ │ │ ├── KryoStrategyCustomReferenceResolverTest.java │ │ │ └── NullReferenceResolver.java │ │ └── test │ │ ├── AnotherNonSerializableObject.java │ │ ├── MutatingObjectSerializer.java │ │ ├── NonSerializableObject.java │ │ ├── NonSerializableObjectRegisteredInDefaultConfigFile.java │ │ └── TestUtils.java │ └── resources │ ├── compatible-field-default-serializer.properties │ └── test-subzero-serializers.properties └── subzero-it-base ├── pom.xml └── src └── main └── java └── info └── jerrinot └── subzero └── it ├── BaseSmokeTests.java ├── Configurer.java ├── MapProxy.java └── Person.java /.github/workflows/it.yml: -------------------------------------------------------------------------------- 1 | name: Integration Tests 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ master ] 10 | types: [opened, synchronize, reopened] 11 | 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | 28 | - name: Setup Java JDK 29 | uses: actions/setup-java@v2.3.1 30 | with: 31 | java-version: 8 32 | distribution: 'zulu' 33 | cache: 'maven' 34 | 35 | - name: Build and analyze 36 | run: mvn clean install -P it 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | dependency-reduced-pom.xml 3 | *.iml 4 | target 5 | .metadata 6 | bin/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | script: "mvn clean install -P it" 5 | -------------------------------------------------------------------------------- /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 | 204 | 205 | 206 | 207 | 208 | The Subzero project bundles the following files/projects: 209 | ----- 210 | *Kryo* 211 | License Text: 212 | 213 | Copyright (c) 2008, Nathan Sweet 214 | All rights reserved. 215 | 216 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 217 | 218 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 219 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 220 | * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 221 | 222 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 223 | ----- 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SubZero - Fast Serialization for Hazelcast 2 | 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/info.jerrinot/subzero-all/badge.svg)](https://maven-badges.herokuapp.com/maven-central/info.jerrinot/subzero-all) 4 | [![Join the chat at https://gitter.im/subzero-hz/Lobby](https://badges.gitter.im/subzero-hz/Lobby.svg)](https://gitter.im/subzero-hz/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | SubZero provides dead easy Hazelcast - Kryo integration. No coding required. 7 | 8 | ## Why? 9 | Kryo is a popular serialization library. It's [super-fast](https://github.com/eishay/jvm-serializers/wiki) yet easy-to-use. 10 | It does not pollute your domain model and it can even serialize classes 11 | which are not marked as `Serializable`. 12 | 13 | Hazelcast has no out-of-the box support for Kryo. It's [rather easy](http://blog.hazelcast.com/kryo-serializer/) to 14 | integrate it, however it means everyone has to write the same code and 15 | face the [same bugs](https://github.com/hazelcast/hazelcast/issues?utf8=%E2%9C%93&q=is%3Aissue%20kryo). 16 | 17 | SubZero aims to make Kryo - Hazelcast integration as simple as possible. 18 | 19 | 20 | ## How to Use SubZero? 21 | 22 | ### Use SubZero for all classes 23 | SubZero will completely replace Java serialization. Hazelcast internal 24 | serializers will still take precedence. 25 | 26 | #### Declarative Configuration: 27 | Insert this snippet into your Hazelcast configuration XML: 28 | ````xml 29 | 30 | 31 | 32 | info.jerrinot.subzero.Serializer 33 | 34 | 35 | 36 | ```` 37 | 38 | #### Programmatic Configuration: 39 | ````java 40 | Config config = new Config(); 41 | SubZero.useAsGlobalSerializer(config); 42 | HazelcastInstance hz = Hazelcast.newHazelcastInstance(config); 43 | ```` 44 | 45 | ### Use SubZero for selected classes only 46 | In this mode Hazelcast will use SubZero for selected classes only. 47 | 48 | #### Declarative Configuration: 49 | ````xml 50 | 51 | 52 | 54 | 56 | 57 | 58 | ```` 59 | 60 | #### Programmatic Configuration: 61 | ````java 62 | Config config = new Config(); 63 | SubZero.useForClasses(config, Foo.class, Bar.class); 64 | HazelcastInstance hz = Hazelcast.newHazelcastInstance(config); 65 | ```` 66 | 67 | All cluster members have to use SubZero for the same types and the types 68 | have to be declared in the same order. 69 | 70 | ## Maven Coordinates 71 | SubZero is available in Maven Central. Just insert this snippet into 72 | pom.xml and you are ready to roll! 73 | ````xml 74 | 75 | info.jerrinot 76 | subzero-all 77 | 0.11 78 | 79 | ```` 80 | This version has all dependencies packaged inside. You can also use a 81 | version with regular dependencies: 82 | ````xml 83 | 84 | info.jerrinot 85 | subzero-core 86 | 0.11 87 | 88 | ```` 89 | 90 | ## Configuration Options 91 | - System property `subzero.buffer.size.kb` sets buffer size for Kryo. 92 | Default value: 16KB 93 | - System property `subzero.base.type.id` sets base for auto-generated 94 | type id 95 | - System property `subzero.referenceresolver.class` sets the ReferenceResolver implementation. Default value: `com.esotericsoftware.kryo.util.MapReferenceResolver` 96 | 97 | ## Custom Kryo Serializers 98 | SubZero can use custom Kryo serializers. 99 | ### Simple Registration 100 | Just create a file `subzero-serializers.properties` 101 | and have it on a classpath of your project. SubZero expects the property file to have the following format: 102 | ```` 103 | some.package.YouDomainClass=other.package.KryoSerializer 104 | ```` 105 | ### Advanced Registration 106 | The simple approach works fine in most cases, but sometimes you do not know 107 | domain classnames up front - for example when the class is 108 | created by a factory - think of `Collections::unmodifiableList` 109 | 110 | In this case it's OK to have just the Kryo serializers in property file. 111 | For example: 112 | ```` 113 | my.package.KryoSerializerClass 114 | ```` 115 | Subzero expects the serializer to have a method `registerSerializers` 116 | which accepts an instance of `Kryo` as its only argument. 117 | 118 | It's up to the serializer to register itself into Kryo. This approach 119 | works for most serializer from [this project](https://github.com/magro/kryo-serializers). 120 | Actually serializers from this project are considered to be well-known and it's ok to use just a classname 121 | without package in the property file. 122 | For example: 123 | ```` 124 | UnmodifiableCollectionsSerializer 125 | ArraysAsListSerializer 126 | ```` 127 | 128 | ### Default Kryo Serializer 129 | Kryo uses [FieldSerializer](https://github.com/EsotericSoftware/kryo/blob/498cd2b9765d3af8c77d3c4b1ede993fe3dd45a7/src/com/esotericsoftware/kryo/serializers/FieldSerializer.java) 130 | by default. Sometimes you want to change it. 131 | 132 | For example when you want support adding new fields then you have to use [CompatibleFieldSerializer](https://github.com/EsotericSoftware/kryo/blob/498cd2b9765d3af8c77d3c4b1ede993fe3dd45a7/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java). 133 | You can do this by using the key `defaultSerializer` in the property file. 134 | 135 | Example: 136 | `defaultSerializer=com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer` 137 | 138 | This mechanism also understands the concept of well-known packages: 139 | Serializers from the Kryo project itself and [Magro serializers](https://github.com/magro/kryo-serializer) 140 | can be referenced just by a simple classname, no need to specify package. This is handy 141 | when using the Subzero-all version which has the Kryo serializers relocated into 142 | another package. 143 | 144 | ## Extensions 145 | SubZero aims to provide the simplest possible way to hook Kryo 146 | serialization into Hazelcast. 147 | 148 | Default SubZero serializer implementation uses auto-generated class 149 | type IDs and relies on a serializer registration order. This means all 150 | your cluster members have to use the same order in Hazelcast serializer 151 | configuration. This can be somewhat fragile. You can make it more robust 152 | by subclassing Serializer and returning a fixed class ID: 153 | ````java 154 | public class HashMapSerializerExample extends AbstractTypeSpecificUserSerializer { 155 | 156 | public HashMapSerializerExample() { 157 | super(HashMap.class); 158 | } 159 | 160 | /** 161 | * TypeId has to be a unique for each registered serializer. 162 | * 163 | * @return TypeId of the class serialized by this serializer 164 | */ 165 | @Override 166 | public int getTypeId() { 167 | return 10000; 168 | } 169 | } 170 | ```` 171 | 172 | ## Hazelcast Compatibility 173 | SubZero is continuously tested with Hazelcast 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 4.0 and 5.0 174 | 175 | ## Requirments 176 | Subzero requires JDK8 or newer. 177 | 178 | ## Known Issues 179 | Subzero-All currently [does not support](https://github.com/jerrinot/subzero/issues/46) 3rd party Kryo serializers. If you need to register a 3rd party serializer then use Subzero-Core. 180 | 181 | ## Further Ideas 182 | - More serialization strategies. Currently Kryo is the only supported 183 | strategy. I would like to add Fast Serialization 184 | - Serializer Generator - SubZero could generate highly-optimized 185 | serializer for simple classes 186 | - AutoPortable - serialize an ordinary class as it implemented 187 | Portable interface 188 | 189 | 190 | 191 | ## Notable Contributors 192 | - [Ashok Koyi](https://github.com/thekalinga) reported a [first bug](https://github.com/jerrinot/subzero/issues/3) and also came up with a solution. 193 | - [Will Neild](https://github.com/wneild) came up with an idea to register [custom Kryo serializers](https://github.com/jerrinot/subzero/issues/6) and contributed to other parts as well. 194 | - [Adrian](https://github.com/acieplak) contributed support for [custom reference resolvers](https://github.com/jerrinot/subzero/pull/25). 195 | - [Tomasz Gawęda](https://github.com/TomaszGaweda) for [Hazelcast 4.x compatibility](https://github.com/jerrinot/subzero/pull/40). 196 | 197 | 198 | ## Disclaimer 199 | This is a community project not affiliated with the Hazelcast project. 200 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | info.jerrinot 6 | subzero 7 | 0.12-SNAPSHOT 8 | pom 9 | 10 | 11 | 3.0.4 12 | 13 | 14 | 15 | subzero-core 16 | subzero-all 17 | 18 | 19 | SubZero 20 | SubZero - Fast Serialization for Hazelcast 21 | https://github.com/jerrinot/subzero 22 | 23 | 24 | 25 | Apache License, Version 2.0 26 | http://www.apache.org/licenses/LICENSE-2.0.txt 27 | 28 | 29 | 30 | 31 | 32 | Jaromir Hamala 33 | jaromir.hamala@gmail.com 34 | 35 | 36 | 37 | 38 | scm:git:git@github.com:jerrinot/subzero.git 39 | scm:git:git@github.com:jerrinot/subzero.git 40 | https://github.com/jerrinot/subzero 41 | HEAD 42 | 43 | 44 | 45 | 1.8 46 | 1.8 47 | 1.8 48 | 1.8 49 | 50 | UTF-8 51 | -Xdoclint:none 52 | 53 | 5.2.0 54 | 3.7 55 | 56 | 4.12 57 | 2.0.2-beta 58 | 1.2.17 59 | 3.5.2 60 | 1.9.5 61 | 2.8.1 62 | 0.42 63 | 64 | 65 | 66 | 67 | it 68 | 69 | subzero-it-base 70 | subzero-all-it-hz36 71 | subzero-all-it-hz37 72 | subzero-all-it-hz38 73 | subzero-all-it-hz39 74 | subzero-all-it-hz310 75 | subzero-all-it-hz311 76 | subzero-all-it-hz312 77 | subzero-all-it-hz40 78 | subzero-all-it-hz50 79 | 80 | 81 | 82 | 83 | release 84 | 85 | 86 | 87 | org.sonatype.plugins 88 | nexus-staging-maven-plugin 89 | true 90 | 91 | ossrh 92 | https://oss.sonatype.org/ 93 | true 94 | 95 | 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-source-plugin 100 | 101 | 102 | attach-sources 103 | 104 | jar-no-fork 105 | 106 | 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-javadoc-plugin 113 | 114 | 115 | attach-javadocs 116 | 117 | jar 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-gpg-plugin 126 | 127 | 128 | sign-artifacts 129 | verify 130 | 131 | sign 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | ide 142 | 143 | false 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-compiler-plugin 150 | 151 | ${java.testTarget.version} 152 | ${java.testTarget.version} 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | org.apache.maven.plugins 165 | maven-enforcer-plugin 166 | 1.4.1 167 | 168 | 169 | 170 | ${java.testTarget.version} 171 | 172 | 173 | 174 | 175 | 176 | enforce-java 177 | 178 | enforce 179 | 180 | 181 | 182 | 183 | 184 | 185 | org.codehaus.mojo 186 | animal-sniffer-maven-plugin 187 | 1.16 188 | 189 | 190 | org.codehaus.mojo.signature 191 | java18 192 | 1.0 193 | 194 | 195 | 196 | 197 | signature-check 198 | verify 199 | 200 | check 201 | 202 | 203 | 204 | 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-compiler-plugin 209 | 3.5.1 210 | 211 | ${java.source.version} 212 | ${java.target.version} 213 | ${java.testSource.version} 214 | ${java.testTarget.version} 215 | 216 | 217 | 218 | 219 | org.apache.maven.plugins 220 | maven-release-plugin 221 | 2.5.3 222 | 223 | true 224 | false 225 | release 226 | deploy 227 | v@{project.version} 228 | true 229 | 230 | 231 | 232 | 233 | org.sonatype.plugins 234 | nexus-staging-maven-plugin 235 | 1.6.7 236 | 237 | 238 | 239 | org.apache.maven.plugins 240 | maven-source-plugin 241 | 3.0.1 242 | 243 | 244 | 245 | org.apache.maven.plugins 246 | maven-javadoc-plugin 247 | 2.10.4 248 | 249 | 250 | 251 | org.apache.maven.plugins 252 | maven-gpg-plugin 253 | 1.6 254 | 255 | 256 | 257 | org.apache.maven.plugins 258 | maven-shade-plugin 259 | 2.4.3 260 | 261 | 262 | 263 | org.apache.maven.plugins 264 | maven-jar-plugin 265 | 3.1.1 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | org.apache.maven.plugins 274 | maven-enforcer-plugin 275 | 276 | 277 | 278 | org.codehaus.mojo 279 | animal-sniffer-maven-plugin 280 | 281 | 282 | 283 | org.apache.maven.plugins 284 | maven-compiler-plugin 285 | 286 | 287 | 288 | org.apache.maven.plugins 289 | maven-release-plugin 290 | 291 | 292 | 293 | 294 | 295 | 296 | ossrh 297 | https://oss.sonatype.org/content/repositories/snapshots 298 | 299 | 300 | 301 | 302 | 303 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | echo Preparing Release 5 | mvn --batch-mode release:prepare -Pit 6 | echo Releasing 7 | mvn release:perform 8 | mvn release:clean -Pit -------------------------------------------------------------------------------- /subzero-all-it-hz310/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz310 11 | 12 | 3.10.6 13 | 14 | 15 | 16 | 17 | info.jerrinot 18 | subzero-it-base 19 | ${project.version} 20 | test 21 | 22 | 23 | 24 | com.hazelcast 25 | hazelcast 26 | ${hazelcast.version} 27 | test 28 | 29 | 30 | 31 | com.hazelcast 32 | hazelcast-client 33 | ${hazelcast.version} 34 | 35 | 36 | 37 | com.hazelcast 38 | hazelcast 39 | ${hazelcast.version} 40 | test 41 | tests 42 | 43 | 44 | 45 | com.hazelcast 46 | hazelcast-client 47 | ${hazelcast.version} 48 | tests 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /subzero-all-it-hz310/src/test/java/info/jerrinot/subzero/it/hz310/Hazelcast310_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz310; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast310_SmokeTest extends BaseSmokeTests { 6 | } 7 | -------------------------------------------------------------------------------- /subzero-all-it-hz311/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz311 11 | 12 | 13 | 3.11 14 | 15 | 16 | 17 | 18 | info.jerrinot 19 | subzero-it-base 20 | ${project.version} 21 | test 22 | 23 | 24 | 25 | com.hazelcast 26 | hazelcast 27 | ${hazelcast.version} 28 | test 29 | 30 | 31 | 32 | com.hazelcast 33 | hazelcast-client 34 | ${hazelcast.version} 35 | 36 | 37 | 38 | com.hazelcast 39 | hazelcast 40 | ${hazelcast.version} 41 | test 42 | tests 43 | 44 | 45 | 46 | com.hazelcast 47 | hazelcast-client 48 | ${hazelcast.version} 49 | tests 50 | 51 | 52 | -------------------------------------------------------------------------------- /subzero-all-it-hz311/src/test/java/info/jerrinot/subzero/it/hz311/Hazelcast311_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz311; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast311_SmokeTest extends BaseSmokeTests { 6 | } 7 | -------------------------------------------------------------------------------- /subzero-all-it-hz312/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz312 11 | 12 | 13 | 3.12.6 14 | 15 | 16 | 17 | 18 | info.jerrinot 19 | subzero-it-base 20 | ${project.version} 21 | test 22 | 23 | 24 | 25 | com.hazelcast 26 | hazelcast 27 | ${hazelcast.version} 28 | test 29 | 30 | 31 | 32 | com.hazelcast 33 | hazelcast-client 34 | ${hazelcast.version} 35 | 36 | 37 | 38 | com.hazelcast 39 | hazelcast 40 | ${hazelcast.version} 41 | test 42 | tests 43 | 44 | 45 | 46 | com.hazelcast 47 | hazelcast-client 48 | ${hazelcast.version} 49 | tests 50 | 51 | 52 | -------------------------------------------------------------------------------- /subzero-all-it-hz312/src/test/java/info/jerrinot/subzero/it/hz312/Hazelcast312_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz312; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast312_SmokeTest extends BaseSmokeTests { 6 | } 7 | -------------------------------------------------------------------------------- /subzero-all-it-hz36/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | jar 10 | 11 | subzero-all-it-hz36 12 | 13 | 14 | 3.6.8 15 | 16 | 17 | 18 | 19 | info.jerrinot 20 | subzero-it-base 21 | ${project.version} 22 | test 23 | 24 | 25 | 26 | com.hazelcast 27 | hazelcast 28 | ${hazelcast.version} 29 | test 30 | 31 | 32 | 33 | com.hazelcast 34 | hazelcast-client 35 | ${hazelcast.version} 36 | test 37 | 38 | 39 | 40 | com.hazelcast 41 | hazelcast 42 | ${hazelcast.version} 43 | test 44 | tests 45 | 46 | 47 | 48 | com.hazelcast 49 | hazelcast-client 50 | ${hazelcast.version} 51 | tests 52 | test 53 | 54 | 55 | -------------------------------------------------------------------------------- /subzero-all-it-hz36/src/test/java/info/jerrinot/subzero/it/hz36/Hazelcast36_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz36; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast36_SmokeTest extends BaseSmokeTests { 6 | } 7 | -------------------------------------------------------------------------------- /subzero-all-it-hz37/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz37 11 | 12 | 3.7.8 13 | 14 | 15 | 16 | 17 | info.jerrinot 18 | subzero-it-base 19 | ${project.version} 20 | test 21 | 22 | 23 | 24 | com.hazelcast 25 | hazelcast 26 | ${hazelcast.version} 27 | test 28 | 29 | 30 | 31 | com.hazelcast 32 | hazelcast-client 33 | ${hazelcast.version} 34 | test 35 | 36 | 37 | 38 | com.hazelcast 39 | hazelcast 40 | ${hazelcast.version} 41 | test 42 | tests 43 | 44 | 45 | 46 | com.hazelcast 47 | hazelcast-client 48 | ${hazelcast.version} 49 | tests 50 | test 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /subzero-all-it-hz37/src/test/java/info/jerrinot/subzero/it/hz37/Hazelcast37_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz37; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast37_SmokeTest extends BaseSmokeTests { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /subzero-all-it-hz38/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz38 11 | 12 | 13 | 3.8.9 14 | 15 | 16 | 17 | 18 | info.jerrinot 19 | subzero-it-base 20 | ${project.version} 21 | test 22 | 23 | 24 | 25 | com.hazelcast 26 | hazelcast 27 | ${hazelcast.version} 28 | test 29 | 30 | 31 | 32 | com.hazelcast 33 | hazelcast-client 34 | ${hazelcast.version} 35 | test 36 | 37 | 38 | 39 | com.hazelcast 40 | hazelcast 41 | ${hazelcast.version} 42 | test 43 | tests 44 | 45 | 46 | 47 | com.hazelcast 48 | hazelcast-client 49 | ${hazelcast.version} 50 | tests 51 | test 52 | 53 | 54 | 55 | 56 | 57 | sonatype-snapshots 58 | Sonatype Snapshot Repository 59 | https://oss.sonatype.org/content/repositories/snapshots 60 | 61 | false 62 | 63 | 64 | true 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /subzero-all-it-hz38/src/test/java/info/jerrinot/subzero/it/hz38/Hazelcast38_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz38; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast38_SmokeTest extends BaseSmokeTests { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /subzero-all-it-hz39/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz39 11 | 12 | 13 | 3.9.4 14 | 15 | 16 | 17 | 18 | info.jerrinot 19 | subzero-it-base 20 | ${project.version} 21 | test 22 | 23 | 24 | 25 | com.hazelcast 26 | hazelcast 27 | ${hazelcast.version} 28 | test 29 | 30 | 31 | 32 | com.hazelcast 33 | hazelcast-client 34 | ${hazelcast.version} 35 | 36 | 37 | 38 | com.hazelcast 39 | hazelcast 40 | ${hazelcast.version} 41 | test 42 | tests 43 | 44 | 45 | 46 | com.hazelcast 47 | hazelcast-client 48 | ${hazelcast.version} 49 | tests 50 | 51 | 52 | -------------------------------------------------------------------------------- /subzero-all-it-hz39/src/test/java/info/jerrinot/subzero/it/hz39/Hazelcast39_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz39; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast39_SmokeTest extends BaseSmokeTests { 6 | } 7 | -------------------------------------------------------------------------------- /subzero-all-it-hz40/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz40 11 | 12 | 13 | 4.0 14 | 15 | 16 | 17 | 18 | info.jerrinot 19 | subzero-it-base 20 | ${project.version} 21 | test 22 | 23 | 24 | com.hazelcast 25 | hazelcast-client 26 | 27 | 28 | 29 | 30 | 31 | com.hazelcast 32 | hazelcast 33 | ${hazelcast.version} 34 | test 35 | 36 | 37 | 38 | com.hazelcast 39 | hazelcast 40 | ${hazelcast.version} 41 | test 42 | tests 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /subzero-all-it-hz40/src/test/java/info/jerrinot/subzero/it/hz40/Hazelcast40_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz40; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast40_SmokeTest extends BaseSmokeTests { 6 | } 7 | -------------------------------------------------------------------------------- /subzero-all-it-hz50/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all-it-hz50 11 | 12 | 13 | 8 14 | 8 15 | 5.0 16 | 17 | 18 | 19 | 20 | info.jerrinot 21 | subzero-it-base 22 | ${project.version} 23 | test 24 | 25 | 26 | com.hazelcast 27 | hazelcast-client 28 | 29 | 30 | 31 | 32 | 33 | com.hazelcast 34 | hazelcast 35 | ${hazelcast.version} 36 | test 37 | 38 | 39 | 40 | com.hazelcast 41 | hazelcast 42 | ${hazelcast.version} 43 | test 44 | tests 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /subzero-all-it-hz50/src/test/java/info/jerrinot/subzero/it/hz50/Hazelcast50_SmokeTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it.hz50; 2 | 3 | import info.jerrinot.subzero.it.BaseSmokeTests; 4 | 5 | public class Hazelcast50_SmokeTest extends BaseSmokeTests { 6 | } 7 | -------------------------------------------------------------------------------- /subzero-all/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-all 11 | 12 | 13 | 14 | info.jerrinot 15 | subzero-core 16 | ${project.version} 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-shade-plugin 25 | 26 | 27 | package 28 | 29 | shade 30 | 31 | 32 | 33 | 34 | com.hazelcast:* 35 | 36 | 37 | 38 | 39 | com.esotericsoftware 40 | info.jerrinot.subzero.relocated.com.esotericsoftware 41 | 42 | 43 | org.objenesis 44 | info.jerrinot.subzero.relocated.org.objenesis 45 | 46 | 47 | org.objectweb 48 | info.jerrinot.subzero.relocated.org.objectweb 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /subzero-all/src/main/java/info/jerrinot/subzero/Placeholder.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | public class Placeholder { 4 | } 5 | -------------------------------------------------------------------------------- /subzero-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-core 11 | subzero-core 12 | jar 13 | 14 | 15 | 16 | com.esotericsoftware 17 | kryo 18 | ${kryo.version} 19 | 20 | 21 | 22 | com.hazelcast 23 | hazelcast 24 | ${hazelcast.version} 25 | 26 | 27 | 28 | com.hazelcast 29 | hazelcast-client 30 | ${hazelcast.version} 31 | true 32 | 33 | 34 | 35 | junit 36 | junit 37 | ${junit.version} 38 | test 39 | 40 | 41 | 42 | com.hazelcast 43 | hazelcast 44 | ${hazelcast.version} 45 | tests 46 | test 47 | 48 | 49 | 50 | com.hazelcast 51 | hazelcast-client 52 | ${hazelcast.version} 53 | test 54 | tests 55 | 56 | 57 | 58 | log4j 59 | log4j 60 | test 61 | ${log4j.version} 62 | 63 | 64 | 65 | org.mockito 66 | mockito-all 67 | ${mockito.version} 68 | test 69 | 70 | 71 | 72 | org.assertj 73 | assertj-core 74 | ${assertj.version} 75 | test 76 | 77 | 78 | 79 | de.javakaffee 80 | kryo-serializers 81 | ${de.javakaffee.version} 82 | test 83 | 84 | 85 | 86 | joda-time 87 | joda-time 88 | ${jodatime.version} 89 | test 90 | 91 | 92 | 93 | net.bytebuddy 94 | byte-buddy 95 | ${bytebuddy.version} 96 | test 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-jar-plugin 105 | 106 | 107 | 108 | test-jar 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/AbstractGlobalUserSerializer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import info.jerrinot.subzero.internal.strategy.GlobalKryoStrategy; 4 | 5 | /** 6 | * This class is for registering Kryo serialization globally with custom serialization for specific types 7 | * programatically rather than through use of subzero-serializers.properties properties file. 8 | * 9 | * 10 | * import static info.jerrinot.subzero.UserSerializerConfig.register; 11 | * [...] 12 | * 13 | * public static class MyGlobalSerializer extends AbstractGlobalUserSerializer { 14 | * 15 | * public MyGlobalSerializer() { 16 | * super(register(MyObject.class, new MyObjectKryoSerializer()) 17 | * .register(MyOtherObject.class, new MyOtherObjectKryoSerializer()); 18 | * } 19 | * 20 | * } 21 | * 22 | * 23 | * or direclty acessing Kryo 24 | * 25 | * 26 | * import static info.jerrinot.subzero.UserSerializerConfig.delegate; 27 | * [...] 28 | * 29 | * public static class MyGlobalDelegateSerializationConfig extends AbstractGlobalUserSerializer { 30 | * public MyGlobalDelegateSerlizationConfig() { 31 | * super(UserSerializerConfig.delegate(new KryoConfigurer() { 32 | * @Override 33 | * public void configure(Kryo kryo) { 34 | * kryo.register(AnotherNonSerializableObject.class, new AnotherNonSerializableObjectKryoSerializer()); 35 | * } 36 | * })); 37 | * } 38 | * } 39 | * 40 | * 41 | */ 42 | public abstract class AbstractGlobalUserSerializer extends AbstractSerializer { 43 | 44 | public AbstractGlobalUserSerializer(UserSerializerConfig.UserSerializationBuilder userSerializer) { 45 | super(new GlobalKryoStrategy(userSerializer)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/AbstractSerializer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.hazelcast.core.HazelcastInstance; 4 | import com.hazelcast.core.HazelcastInstanceAware; 5 | import com.hazelcast.nio.ObjectDataInput; 6 | import com.hazelcast.nio.ObjectDataOutput; 7 | import com.hazelcast.nio.serialization.StreamSerializer; 8 | import info.jerrinot.subzero.internal.strategy.KryoStrategy; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.OutputStream; 13 | 14 | abstract class AbstractSerializer implements StreamSerializer, HazelcastInstanceAware { 15 | private int autoGeneratedTypeId; 16 | private HazelcastInstance hazelcastInstance; 17 | private KryoStrategy strategy; 18 | 19 | AbstractSerializer(KryoStrategy strategy) { 20 | this.strategy = strategy; 21 | } 22 | 23 | @Override 24 | public final void write(ObjectDataOutput out, T object) throws IOException { 25 | strategy.write((OutputStream) out, object); 26 | } 27 | 28 | @Override 29 | public final T read(ObjectDataInput in) throws IOException { 30 | return strategy.read((InputStream) in); 31 | } 32 | 33 | /** 34 | * Override this method to returns your own type ID. 35 | *

36 | * Default implementation relies on serializers registration order - all 37 | * your cluster members have to register SubZero in the same order otherwise 38 | * things will get out-of-sync you you will get tons of serializations errors. 39 | *

40 | * All serializers registered in Hazelcast have to return a unique type ID. 41 | * 42 | * @return serializer type ID. 43 | */ 44 | @Override 45 | public int getTypeId() { 46 | return autoGeneratedTypeId; 47 | } 48 | 49 | @Override 50 | public final void destroy() { 51 | strategy.destroy(hazelcastInstance); 52 | } 53 | 54 | @Override 55 | public final void setHazelcastInstance(HazelcastInstance hazelcastInstance) { 56 | strategy.setHazelcastInstance(hazelcastInstance); 57 | this.hazelcastInstance = hazelcastInstance; 58 | this.autoGeneratedTypeId = strategy.newId(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/AbstractTypeSpecificUserSerializer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import info.jerrinot.subzero.internal.PropertyUserSerializer; 4 | import info.jerrinot.subzero.internal.strategy.TypedKryoStrategy; 5 | 6 | 7 | /** 8 | * This class is for registering Kryo serialization for a type where you need custom serialization and/or you want 9 | * control over the serializable class type ID. 10 | * 11 | * The class is intended to be extended. It optionally takes a custom serializer 12 | * {@link com.esotericsoftware.kryo.Serializer} that will be registered with the internal Kryo serializer instance for 13 | * the provided class. By not providing a custom serializer, the behaviour of the class is to check for a custom 14 | * serializer registration in the subzero-serializers.properties properties file. 15 | * 16 | * 17 | * public static class MySerializer extends AbstractTypeSpecificUserSerializer { 18 | * 19 | * public MySerializer() { 20 | * super(MyObject.class, new MyObjectKryoSerializer()); 21 | * } 22 | * 23 | * } 24 | * 25 | * 26 | * It is also possible to define a static class type ID through overriding {@link #getTypeId()} which would otherwise 27 | * return an auto-generated ID unique to the type. 28 | * 29 | * @param type to register Kyro serialization against 30 | */ 31 | public abstract class AbstractTypeSpecificUserSerializer extends AbstractSerializer { 32 | 33 | public AbstractTypeSpecificUserSerializer(Class clazz, com.esotericsoftware.kryo.Serializer serializer) { 34 | super(new TypedKryoStrategy(clazz, UserSerializerConfig.register(clazz, serializer))); 35 | } 36 | 37 | /** 38 | * @param clazz class this serializer uses. 39 | * 40 | */ 41 | public AbstractTypeSpecificUserSerializer(Class clazz) { 42 | super(new TypedKryoStrategy(clazz, PropertyUserSerializer.INSTANCE)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/ClassFactory.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | /** 4 | * Factory for creating domain classes 5 | * 6 | * This is useful when a domain class cannot be instantiated directly, 7 | * but a factory method has to be used. 8 | * 9 | */ 10 | public interface ClassFactory { 11 | Class createClass(); 12 | } 13 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/KryoConfigurer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | 5 | /** 6 | * This class is for configuring direclty Kryo 7 | * 8 | * 9 | * import static info.jerrinot.subzero.UserSerializerConfig.delegate; 10 | * [...] 11 | * 12 | * public static class MyGlobalDelegateSerializationConfig extends AbstractGlobalUserSerializer { 13 | * public MyGlobalDelegateSerlizationConfig() { 14 | * super(UserSerializerConfig.delegate(new KryoConfigurer() { 15 | * @Override 16 | * public void configure(Kryo kryo) { 17 | * kryo.register(AnotherNonSerializableObject.class, new AnotherNonSerializableObjectKryoSerializer()); 18 | * } 19 | * })); 20 | * } 21 | * } 22 | * 23 | * 24 | */ 25 | public interface KryoConfigurer { 26 | 27 | void configure(Kryo kryo); 28 | } 29 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/Serializer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import info.jerrinot.subzero.internal.PropertyUserSerializer; 4 | import info.jerrinot.subzero.internal.strategy.GlobalKryoStrategy; 5 | import info.jerrinot.subzero.internal.strategy.TypedKryoStrategy; 6 | 7 | public final class Serializer extends AbstractSerializer { 8 | 9 | Serializer() { 10 | super(new GlobalKryoStrategy(PropertyUserSerializer.INSTANCE)); 11 | } 12 | 13 | /** 14 | * @param clazz class this serializer uses. 15 | * 16 | */ 17 | public Serializer(Class clazz) { 18 | super(new TypedKryoStrategy(clazz, PropertyUserSerializer.INSTANCE)); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/SerializerConfigurer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | public interface SerializerConfigurer { 4 | void configure(Class clazz, Object serializer); 5 | } 6 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/SubZero.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.hazelcast.client.config.ClientConfig; 4 | import com.hazelcast.config.Config; 5 | import com.hazelcast.config.GlobalSerializerConfig; 6 | import com.hazelcast.config.SerializationConfig; 7 | import com.hazelcast.config.SerializerConfig; 8 | 9 | /** 10 | * Convenient class for dead-simple SubZero injection into Hazelcast configuration. 11 | * 12 | * This is the simplest way to use SubZero when you use Hazelcast programmatic configuration. 13 | * 14 | * 15 | */ 16 | public final class SubZero { 17 | 18 | private SubZero() { 19 | 20 | } 21 | 22 | /** 23 | * Use SubZero as a global serializer. 24 | * 25 | * This method configures Hazelcast to delegate a class serialization to SubZero when the class 26 | * has no explicit strategy configured. Uses {@link Serializer} serializer implementation 27 | * internally. 28 | * 29 | * @param config Hazelcast configuration to inject SubZero into 30 | * @return Hazelcast configuration. 31 | */ 32 | public static Config useAsGlobalSerializer(Config config) { 33 | return useAsGlobalSerializerInternal(config, Serializer.class); 34 | } 35 | 36 | /** 37 | * Use SubZero as a global serializer. 38 | * 39 | * This method configures Hazelcast to delegate a class serialization to SubZero when the class 40 | * has no explicit strategy configured. 41 | * 42 | * @param config Hazelcast configuration to inject SubZero into 43 | * @param serializerClazz Class of global serializer implementation to use 44 | * @return Hazelcast configuration. 45 | */ 46 | public static Config useAsGlobalSerializer(Config config, Class serializerClazz) { 47 | SerializationConfig serializationConfig = config.getSerializationConfig(); 48 | injectSubZero(serializationConfig, serializerClazz); 49 | return config; 50 | } 51 | 52 | /** 53 | * Use SubZero as a global serializer. 54 | * 55 | * This method configures Hazelcast to delegate a class serialization to SubZero when the class 56 | * has no explicit strategy configured. Uses {@link Serializer} serializer implementation 57 | * internally. 58 | * 59 | * This method it intended to be used to configure {@link ClientConfig} instances, but 60 | * I do not want to create a hard-dependency on Hazelcast Client module. 61 | * 62 | * @param config Hazelcast configuration to inject SubZero into 63 | * @return Hazelcast configuration. 64 | */ 65 | public static T useAsGlobalSerializer(T config) { 66 | return useAsGlobalSerializerInternal(config, Serializer.class); 67 | } 68 | 69 | /** 70 | * Use SubZero as a global serializer. 71 | * 72 | * This method configures Hazelcast to delegate a class serialization to SubZero when the class 73 | * has no explicit strategy configured. 74 | * 75 | * This method it intended to be used to configure {@link ClientConfig} instances, but 76 | * I do not want to create a hard-dependency on Hazelcast Client module. 77 | * 78 | * @param config Hazelcast configuration to inject SubZero into 79 | * @param serializerClazz Class of global serializer implementation to use 80 | * @return Hazelcast configuration. 81 | */ 82 | public static T useAsGlobalSerializer(T config, Class serializerClazz) { 83 | return useAsGlobalSerializerInternal(config, serializerClazz); 84 | } 85 | 86 | private static T useAsGlobalSerializerInternal(T config, Class serializerClazz) { 87 | SerializationConfig serializationConfig = extractSerializationConfig(config); 88 | injectSubZero(serializationConfig, serializerClazz); 89 | return config; 90 | } 91 | 92 | private static SerializationConfig extractSerializationConfig(Object config) { 93 | String className = config.getClass().getName(); 94 | SerializationConfig serializationConfig; 95 | if (className.equals("com.hazelcast.client.config.ClientConfig")) { 96 | ClientConfig clientConfig = (ClientConfig) config; 97 | serializationConfig = clientConfig.getSerializationConfig(); 98 | } else if (className.equals("com.hazelcast.config.Config")) { 99 | Config memberConfig = (Config) config; 100 | serializationConfig = memberConfig.getSerializationConfig(); 101 | } else { 102 | throw new IllegalArgumentException("Unknown configuration object " + config); 103 | } 104 | return serializationConfig; 105 | } 106 | 107 | private static void injectSubZero(SerializationConfig serializationConfig, Class serializerClazz) { 108 | GlobalSerializerConfig globalSerializerConfig = serializationConfig.getGlobalSerializerConfig(); 109 | if (globalSerializerConfig == null) { 110 | globalSerializerConfig = new GlobalSerializerConfig(); 111 | serializationConfig.setGlobalSerializerConfig(globalSerializerConfig); 112 | } 113 | globalSerializerConfig.setClassName(serializerClazz.getName()).setOverrideJavaSerialization(true); 114 | } 115 | 116 | /** 117 | * Use SubZero as a serializer for selected classes only. 118 | * 119 | * @param config Hazelcast configuration to inject SubZero into 120 | * @param classes classes Hazelcast should serialize via SubZero 121 | * @return Hazelcast configuration 122 | */ 123 | public static Config useForClasses(Config config, Class...classes) { 124 | return useForClassesInternal(config, classes); 125 | } 126 | 127 | /** 128 | * Use SubZero as a serializer for selected classes only. 129 | * 130 | * This method it intended to be used to configure {@link ClientConfig} instances, but 131 | * I do not want to create a hard-dependency on Hazelcast Client module. 132 | * 133 | * @param config Hazelcast configuration to inject SubZero into 134 | * @param classes classes Hazelcast should serialize via SubZero 135 | * @return Hazelcast configuration 136 | */ 137 | public static T useForClasses(T config, Class...classes) { 138 | return useForClassesInternal(config, classes); 139 | } 140 | 141 | private static T useForClassesInternal(T config, Class...classes) { 142 | SerializationConfig serializationConfig = extractSerializationConfig(config); 143 | for (Class clazz : classes) { 144 | SerializerConfig serializerConfig = new SerializerConfig(); 145 | Serializer serializer = new Serializer(clazz); 146 | serializerConfig.setImplementation(serializer); 147 | serializerConfig.setTypeClass(clazz); 148 | serializationConfig.addSerializerConfig(serializerConfig); 149 | } 150 | return config; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/UserSerializer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.esotericsoftware.kryo.*; 4 | import com.esotericsoftware.kryo.Serializer; 5 | 6 | /** 7 | * Hook for registering customer Kryo serializers. 8 | * 9 | * It's suitable for scenario where property-based registration of custom serializers is not suitable. 10 | * It gives you control on how {@link com.esotericsoftware.kryo.Serializer} instances are created. 11 | * 12 | * In most cases you do not need to implement this interface yourself: You can use the 13 | * {@link UserSerializerConfig#register(Class, Serializer)} static method. 14 | * 15 | * 16 | * import static info.jerrinot.subzero.UserSerializerConfig.register; 17 | * [...] 18 | * 19 | * public static class MySerializer extends Serializer { 20 | * 21 | * public MySerializer() { 22 | * super(MyObject.class); 23 | * } 24 | * 25 | * @Override 26 | * public UserSerializer userSerializers() { 27 | * return register(MyObject.class, new MyCustomKryoSerializer()); 28 | * } 29 | * } 30 | * 31 | * 32 | */ 33 | public interface UserSerializer { 34 | 35 | /** 36 | * Register single serializer. This is useful when SubZero is configured to serializer just a single type. 37 | * It allows implementations to provide a fail-fast semantic when requested type is not known. 38 | * 39 | * @param kryo 40 | * @param clazz 41 | */ 42 | void registerSingleSerializer(Kryo kryo, Class clazz); 43 | 44 | /** 45 | * Register all serializers. It's called when the exact type is not known up-front. This is the case 46 | * of Global Subzero serializers and also when a single type is registered declaratively 47 | * in pre-3.8 Hazelcast versions. 48 | * 49 | * @param kryo 50 | */ 51 | void registerAllSerializers(Kryo kryo); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/UserSerializerConfig.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.Serializer; 5 | 6 | import java.util.LinkedHashMap; 7 | import java.util.Map; 8 | 9 | public final class UserSerializerConfig { 10 | public static UserSerializationBuilder register(Class clazz, Serializer serializer) { 11 | return new UserSerializationBuilder(clazz, serializer); 12 | } 13 | 14 | public static UserSerializationBuilder delegate(KryoConfigurer kryoConfigurer) { 15 | return new UserSerializationBuilder(kryoConfigurer); 16 | } 17 | 18 | public final static class UserSerializationBuilder implements UserSerializer { 19 | private Map maps = new LinkedHashMap(); 20 | private KryoConfigurer kryoConfigurer = null; 21 | 22 | private UserSerializationBuilder(Class clazz, com.esotericsoftware.kryo.Serializer serializer) { 23 | maps.put(clazz, serializer); 24 | } 25 | 26 | private UserSerializationBuilder(KryoConfigurer kryoConfigurer) { 27 | this.kryoConfigurer = kryoConfigurer; 28 | } 29 | 30 | public UserSerializationBuilder register(Class clazz, Serializer serializer) { 31 | com.esotericsoftware.kryo.Serializer previousSerializer = maps.put(clazz, serializer); 32 | if (previousSerializer != null) { 33 | throw new IllegalArgumentException("There is already " + previousSerializer 34 | + " configured for class " + clazz); 35 | } 36 | return this; 37 | } 38 | 39 | @Override 40 | public void registerSingleSerializer(Kryo kryo, Class clazz) { 41 | com.esotericsoftware.kryo.Serializer serializer = maps.get(clazz); 42 | if (serializer == null) { 43 | throw new IllegalArgumentException("There is no custom Kryo Serializer configured for " + clazz); 44 | } 45 | kryo.register(clazz, serializer); 46 | } 47 | 48 | @Override 49 | public void registerAllSerializers(Kryo kryo) { 50 | for (Map.Entry entry : maps.entrySet()) { 51 | Class clazz = entry.getKey(); 52 | com.esotericsoftware.kryo.Serializer serializer = entry.getValue(); 53 | kryo.register(clazz, serializer); 54 | } 55 | if (kryoConfigurer != null) 56 | kryoConfigurer.configure(kryo); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/configurers/EnableSyntheticFields.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.configurers; 2 | 3 | import com.esotericsoftware.kryo.serializers.FieldSerializer; 4 | import info.jerrinot.subzero.SerializerConfigurer; 5 | 6 | public final class EnableSyntheticFields implements SerializerConfigurer { 7 | @Override 8 | public void configure(Class clazz, Object serializer) { 9 | if (serializer instanceof FieldSerializer) { 10 | ((FieldSerializer) serializer).getFieldSerializerConfig().setIgnoreSyntheticFields(false); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/example/HashMapSerializerExample.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.example; 2 | 3 | import info.jerrinot.subzero.AbstractTypeSpecificUserSerializer; 4 | 5 | import java.util.HashMap; 6 | 7 | /** 8 | * Example of a custom SubZero serializer for a specific type. It returns a constant 9 | * {@link #getTypeId()} hence it does not rely on serializer configuration order. 10 | *
11 | * This is how you would use this serializer in Hazelcast configuration: 12 | *

13 |  * {@code
14 |  * 
15 |  *   
16 |  *     
17 |  *   
18 |  * 
19 |  * }
20 |  * 
21 | */ 22 | public class HashMapSerializerExample extends AbstractTypeSpecificUserSerializer { 23 | 24 | public HashMapSerializerExample() { 25 | super(HashMap.class); 26 | } 27 | 28 | /** 29 | * TypeId has to be a unique for each registered serializer. 30 | * 31 | * @return TypeId of the class serialized by this serializer 32 | */ 33 | @Override 34 | public int getTypeId() { 35 | return 10000; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/ClassLoaderUtils.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal; 2 | 3 | import com.hazelcast.client.config.ClientConfig; 4 | import com.hazelcast.config.Config; 5 | import com.hazelcast.core.HazelcastInstance; 6 | 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | 10 | public class ClassLoaderUtils { 11 | private static final boolean DEBUG_CLASSLOADING = Boolean.getBoolean("subzero.debug.classloading"); 12 | private static final Method loadClassMethodRef = getClassLoadMethod(); 13 | 14 | public static ClassLoader getConfiguredClassLoader(HazelcastInstance hz) { 15 | try { 16 | return tryToGetClassLoader(hz); 17 | } catch (RuntimeException e) { 18 | if (DEBUG_CLASSLOADING) { 19 | throw e; 20 | } 21 | return null; 22 | } 23 | } 24 | 25 | private static ClassLoader tryToGetClassLoader(HazelcastInstance hz) { 26 | try { 27 | Config config = hz.getConfig(); 28 | return config.getClassLoader(); 29 | } catch (UnsupportedOperationException e) { 30 | //ok, this is a client instance -> it does not support getConfig() 31 | try { 32 | Method getClientConfigMethod = hz.getClass().getMethod("getClientConfig"); 33 | ClientConfig clientConfig = (ClientConfig) getClientConfigMethod.invoke(hz); 34 | return clientConfig.getClassLoader(); 35 | } catch (NoSuchMethodException e1) { 36 | throw new IllegalArgumentException("Unknown instance object " + hz, e1); 37 | } catch (IllegalAccessException e1) { 38 | throw new IllegalArgumentException("Unknown instance object " + hz, e1); 39 | } catch (InvocationTargetException e1) { 40 | throw new IllegalArgumentException("Unknown instance object " + hz, e1); 41 | } 42 | } 43 | } 44 | 45 | public static Class loadClass(String className, ClassLoader classLoader) throws InvocationTargetException, IllegalAccessException { 46 | return (Class) loadClassMethodRef.invoke(null, classLoader, className); 47 | } 48 | 49 | private static Method getClassLoadMethod() { 50 | Class classLoaderUtilClass = getClassLoaderUtilClass(); 51 | try { 52 | return classLoaderUtilClass.getDeclaredMethod("loadClass", ClassLoader.class, String.class); 53 | } catch (NoSuchMethodException e) { 54 | throw new IllegalStateException(e); 55 | } 56 | } 57 | 58 | private static Class getClassLoaderUtilClass() { 59 | String[] possibleNames = { 60 | "com.hazelcast.nio.ClassLoaderUtil", 61 | "com.hazelcast.internal.nio.ClassLoaderUtil" 62 | }; 63 | 64 | for (String possibleName : possibleNames) { 65 | Class loadedClass = loadClass(possibleName); 66 | if (loadedClass != null) return loadedClass; 67 | } 68 | throw new IllegalStateException("unable to load ClassLoaderUtil"); 69 | } 70 | 71 | private static Class loadClass(String name) { 72 | try { 73 | return Class.forName(name); 74 | } catch (ClassNotFoundException e) { 75 | return null; 76 | } catch (NoClassDefFoundError e) { 77 | return null; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/IdGeneratorUtils.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal; 2 | 3 | import com.hazelcast.core.HazelcastInstance; 4 | 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.ConcurrentMap; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | public class IdGeneratorUtils { 10 | private static final int BASE_ID = Integer.getInteger("subzero.base.type.id", 6000); 11 | 12 | private static ConcurrentHashMap counterMap = 13 | new ConcurrentHashMap(); 14 | 15 | public static int idForType(HazelcastInstance hz, Class type) { 16 | IdSequence idSequence = getOrCreateSequence(hz); 17 | return idSequence.idFor(type); 18 | } 19 | 20 | public static int globalId(HazelcastInstance hz) { 21 | IdSequence idSequence = getOrCreateSequence(hz); 22 | return idSequence.idFor(hz.getClass()); 23 | } 24 | 25 | public static void instanceDestroyed(HazelcastInstance hz) { 26 | counterMap.remove(hz); 27 | } 28 | 29 | private static IdSequence getOrCreateSequence(HazelcastInstance hazelcastInstance) { 30 | IdSequence currentSequence = counterMap.get(hazelcastInstance); 31 | if (currentSequence != null) { 32 | return currentSequence; 33 | } 34 | IdSequence newSequence = new IdSequence(); 35 | currentSequence = counterMap.putIfAbsent(hazelcastInstance, newSequence); 36 | return currentSequence == null ? newSequence : currentSequence; 37 | } 38 | 39 | private static class IdSequence { 40 | private final ConcurrentMap, Integer> knownTypes = new ConcurrentHashMap<>(); 41 | private final AtomicInteger counter = new AtomicInteger(BASE_ID); 42 | 43 | private int idFor(Class clazz) { 44 | return knownTypes.computeIfAbsent(clazz, (ignored) -> counter.incrementAndGet()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/PostProcessingSerializerFactory.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.Serializer; 5 | import com.esotericsoftware.kryo.SerializerFactory; 6 | import info.jerrinot.subzero.SerializerConfigurer; 7 | 8 | public final class PostProcessingSerializerFactory implements SerializerFactory { 9 | private final SerializerFactory delegate; 10 | private final SerializerConfigurer configurer; 11 | 12 | public PostProcessingSerializerFactory(SerializerFactory delegate, SerializerConfigurer configurer) { 13 | this.delegate = delegate; 14 | this.configurer = configurer; 15 | } 16 | 17 | @Override 18 | public Serializer newSerializer(Kryo kryo, Class type) { 19 | Serializer serializer = delegate.newSerializer(kryo, type); 20 | configurer.configure(type, serializer); 21 | return serializer; 22 | } 23 | 24 | @Override 25 | public boolean isSupported(Class type) { 26 | return delegate.isSupported(type); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/PropertyUserSerializer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.Serializer; 5 | import com.esotericsoftware.kryo.SerializerFactory; 6 | import com.esotericsoftware.kryo.serializers.FieldSerializer; 7 | import info.jerrinot.subzero.ClassFactory; 8 | import info.jerrinot.subzero.SerializerConfigurer; 9 | import info.jerrinot.subzero.UserSerializer; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.io.InputStreamReader; 15 | import java.lang.reflect.Constructor; 16 | import java.lang.reflect.InvocationTargetException; 17 | import java.lang.reflect.Method; 18 | import java.util.ArrayList; 19 | import java.util.LinkedHashMap; 20 | import java.util.LinkedHashSet; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.Set; 24 | 25 | import static java.lang.System.getProperty; 26 | 27 | public final class PropertyUserSerializer implements UserSerializer { 28 | private static final String CUSTOM_CONFIG_PROPERTY_NAME = "subzero.custom.serializers.config.filename"; 29 | private static final String DEFAULT_CONFIG_FILENAME = "subzero-serializers.properties"; 30 | private static final String DEFAULT_SERIALIZER_CONFIG_KEY = "defaultSerializer"; 31 | private static final String SERIALIZER_CONFIGURERS_CONFIG_KEY = "serializerConfigurers"; 32 | private static final String WELL_KNOWN_PACKAGES[] = { 33 | "de.javakaffee.kryoserializers", 34 | "com.esotericsoftware.kryo.serializers", 35 | "info.jerrinot.subzero.relocated.com.esotericsoftware.kryo.serializers", 36 | "info.jerrinot.subzero.configurers"}; 37 | 38 | 39 | private static Map customerSerializers; 40 | private static Set specialSerializersRegistrationMethod; 41 | private static Class defaultSerializerClass; 42 | private static List serializerConfigurers; 43 | 44 | public static final PropertyUserSerializer INSTANCE = new PropertyUserSerializer(); 45 | 46 | private PropertyUserSerializer() { 47 | 48 | } 49 | 50 | static { 51 | reinitialize(); 52 | } 53 | 54 | //public for testing only 55 | public static void reinitialize() { 56 | customerSerializers = new LinkedHashMap(); 57 | serializerConfigurers = new ArrayList(); 58 | specialSerializersRegistrationMethod = new LinkedHashSet(); 59 | String configFilename = getProperty(CUSTOM_CONFIG_PROPERTY_NAME, DEFAULT_CONFIG_FILENAME); 60 | initCustomSerializers(configFilename); 61 | } 62 | 63 | private static void initCustomSerializers(String configFilename) { 64 | ClassLoader classLoader = PropertyUserSerializer.class.getClassLoader(); 65 | InputStream configStream = classLoader.getResourceAsStream(configFilename); 66 | if (configStream == null) { 67 | return; 68 | } 69 | BufferedReader reader = new BufferedReader(new InputStreamReader(configStream)); 70 | String line; 71 | try { 72 | while ((line = reader.readLine()) != null) { 73 | readLineAndRegister(line); 74 | } 75 | } catch (IOException ex) { 76 | throw new IllegalStateException(ex); 77 | } 78 | } 79 | 80 | private static void readLineAndRegister(String line) { 81 | line = line.trim(); 82 | if (line.startsWith("#")) { 83 | return; 84 | } 85 | String[] split = line.split("="); 86 | if (split.length == 2) { 87 | String domainClassName = split[0].trim(); 88 | String className = split[1].trim(); 89 | if (DEFAULT_SERIALIZER_CONFIG_KEY.equals(domainClassName)) { 90 | defaultSerializerClass = findClass(className); 91 | } else if (SERIALIZER_CONFIGURERS_CONFIG_KEY.equals(domainClassName)) { 92 | Class clazz = findClass(className); 93 | SerializerConfigurer configurer = createNewInstance(clazz); 94 | serializerConfigurers.add(configurer); 95 | } else { 96 | addNewSerializer(domainClassName, className); 97 | } 98 | } else if (split.length == 1) { 99 | String serializerClass = split[0]; 100 | addNewSpecialSerializer(serializerClass); 101 | } else { 102 | throw new IllegalStateException("Invalid property " + line); 103 | } 104 | } 105 | 106 | private static void addNewSpecialSerializer(String serializerClassName) { 107 | Class serializerClass = findClass(serializerClassName); 108 | Method registrationMethod; 109 | try { 110 | registrationMethod = serializerClass.getMethod("registerSerializers", Kryo.class); 111 | } catch (NoSuchMethodException e) { 112 | String className = WellKnownClassesRepository.findDomainClassNameForWellKnownSerializer(serializerClass); 113 | if (className != null) { 114 | addNewSerializer(className, serializerClassName); 115 | return; 116 | } 117 | throw new IllegalStateException("Serializer " + serializerClassName 118 | + " does not have expected method 'registerSerializers()': ", e); 119 | } 120 | specialSerializersRegistrationMethod.add(registrationMethod); 121 | } 122 | 123 | private static Class findClass(String classname) { 124 | Class clazz; 125 | try { 126 | clazz = (Class) Class.forName(classname); 127 | return clazz; 128 | } catch (ClassNotFoundException e) { 129 | //ok, let's try to find prefix a well known package 130 | if (classname.indexOf('.') == -1) { 131 | for (String wellKnownPackage : WELL_KNOWN_PACKAGES) { 132 | String enrichedClassName = wellKnownPackage + "." + classname; 133 | try { 134 | clazz = (Class) Class.forName(enrichedClassName); 135 | return clazz; 136 | } catch (ClassNotFoundException e1) { 137 | //ignored, we will throw IllegalStateException bellow 138 | } 139 | } 140 | } 141 | throw new IllegalStateException("Class " + classname + " not found", e); 142 | } 143 | } 144 | 145 | private static void addNewSerializer(String domainClassName, String serializerClassName) { 146 | try { 147 | Class domainClazz = Class.forName(domainClassName); 148 | if (ClassFactory.class.isAssignableFrom(domainClazz)) { 149 | //special case where the domain class is not the actual class, but rather 150 | //a factory to create other classes 151 | ClassFactory factory = (ClassFactory) domainClazz.newInstance(); 152 | domainClazz = factory.createClass(); 153 | } 154 | Class serializerClass = findClass(serializerClassName); 155 | Serializer serializer = createNewInstance(serializerClass); 156 | 157 | customerSerializers.put(domainClazz, serializer); 158 | } catch (ClassNotFoundException e) { 159 | throw new IllegalStateException(e); 160 | } catch (InstantiationException e) { 161 | throw new IllegalStateException(e); 162 | } catch (IllegalAccessException e) { 163 | throw new IllegalStateException(e); 164 | } 165 | } 166 | 167 | private static T createNewInstance(Class clazz) { 168 | try { 169 | Constructor constructor = (Constructor) clazz.getConstructor(); 170 | return constructor.newInstance(); 171 | } catch (NoSuchMethodException e) { 172 | throw new IllegalStateException(e); 173 | } catch (IllegalAccessException e) { 174 | throw new IllegalStateException(e); 175 | } catch (InstantiationException e) { 176 | throw new IllegalStateException(e); 177 | } catch (InvocationTargetException e) { 178 | throw new IllegalStateException(e); 179 | } 180 | } 181 | 182 | @Override 183 | public void registerSingleSerializer(Kryo kryo, Class clazz) { 184 | //we delegate to registerAllSerializers as we always want to register all serializers found 185 | //in the property file 186 | registerAllSerializers(kryo); 187 | } 188 | 189 | @Override 190 | public void registerAllSerializers(Kryo kryo) { 191 | for (Map.Entry entry : customerSerializers.entrySet()) { 192 | Class clazz = entry.getKey(); 193 | Serializer serializer = entry.getValue(); 194 | for (SerializerConfigurer configurer : serializerConfigurers) { 195 | configurer.configure(clazz, configurer); 196 | } 197 | kryo.register(clazz, serializer); 198 | } 199 | registerSpecialSerializers(kryo); 200 | registerDefaultSerializer(kryo); 201 | } 202 | 203 | private void registerDefaultSerializer(Kryo kryo) { 204 | if (defaultSerializerClass != null || !serializerConfigurers.isEmpty()) { 205 | Class serializerClass = defaultSerializerClass == null 206 | ? FieldSerializer.class 207 | : defaultSerializerClass; 208 | 209 | SerializerFactory serializerFactory = new SerializerFactory.ReflectionSerializerFactory(serializerClass); 210 | if (!serializerConfigurers.isEmpty()) { 211 | SerializerConfigurer configurer = serializerConfigurers.get(0); 212 | serializerFactory = new PostProcessingSerializerFactory(serializerFactory, configurer); 213 | } 214 | kryo.setDefaultSerializer(serializerFactory); 215 | } 216 | } 217 | 218 | private void registerSpecialSerializers(Kryo kryo) { 219 | for (Method registrationMethod : specialSerializersRegistrationMethod) { 220 | try { 221 | registrationMethod.invoke(null, kryo); 222 | } catch (IllegalAccessException e) { 223 | throw new IllegalStateException(e); 224 | } catch (InvocationTargetException e) { 225 | throw new IllegalStateException(e); 226 | } 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/WellKnownClassesRepository.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.GregorianCalendar; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | class WellKnownClassesRepository { 11 | private static final Map serializer2DomainClass = new HashMap(); 12 | 13 | static { 14 | //jdk stuff 15 | serializer2DomainClass.put("de.javakaffee.kryoserializers.ArraysAsListSerializer", Arrays.asList( "" ).getClass().getName()); 16 | serializer2DomainClass.put("de.javakaffee.kryoserializers.CollectionsEmptyListSerializer", Collections.EMPTY_LIST.getClass().getName()); 17 | serializer2DomainClass.put("de.javakaffee.kryoserializers.CollectionsEmptyMapSerializer", Collections.EMPTY_MAP.getClass().getName()); 18 | serializer2DomainClass.put("de.javakaffee.kryoserializers.CollectionsEmptySetSerializer", Collections.EMPTY_SET.getClass().getName()); 19 | serializer2DomainClass.put("de.javakaffee.kryoserializers.CollectionsSingletonListSerializer", Collections.singletonList( "" ).getClass().getName()); 20 | serializer2DomainClass.put("de.javakaffee.kryoserializers.CollectionsSingletonSetSerializer", Collections.singleton( "" ).getClass().getName()); 21 | serializer2DomainClass.put("de.javakaffee.kryoserializers.CollectionsSingletonMapSerializer", Collections.singletonMap( "", "" ).getClass().getName()); 22 | serializer2DomainClass.put("de.javakaffee.kryoserializers.GregorianCalendarSerializer", GregorianCalendar.class.getName()); 23 | serializer2DomainClass.put("de.javakaffee.kryoserializers.JdkProxySerializer", InvocationHandler.class.getName()); 24 | } 25 | 26 | static String findDomainClassNameForWellKnownSerializer(Class clazz) { 27 | return serializer2DomainClass.get(clazz.getName()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/DelegatingClassResolver.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import com.esotericsoftware.kryo.util.DefaultClassResolver; 4 | import info.jerrinot.subzero.internal.ClassLoaderUtils; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | /** 9 | * Delegates class loading to Hazelcast -> SubZero will use the same strategy 10 | * as Hazelcast. 11 | * 12 | */ 13 | final class DelegatingClassResolver extends DefaultClassResolver { 14 | 15 | private final ClassLoader classLoader; 16 | 17 | public DelegatingClassResolver(ClassLoader classLoader) { 18 | this.classLoader = classLoader; 19 | } 20 | 21 | @Override 22 | protected Class getTypeByName(String className) { 23 | try { 24 | return ClassLoaderUtils.loadClass(className, classLoader); 25 | } catch (InvocationTargetException e) { 26 | if (e.getCause() instanceof ClassNotFoundException) { 27 | return null; 28 | } 29 | throw new IllegalStateException(e); 30 | } catch (IllegalAccessException e) { 31 | throw new IllegalStateException(e); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/GlobalKryoStrategy.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import info.jerrinot.subzero.UserSerializer; 7 | import info.jerrinot.subzero.internal.IdGeneratorUtils; 8 | 9 | public final class GlobalKryoStrategy extends KryoStrategy { 10 | private final UserSerializer userSerializer; 11 | 12 | public GlobalKryoStrategy(UserSerializer registrations) { 13 | this.userSerializer = registrations; 14 | } 15 | 16 | @Override 17 | public void registerCustomSerializers(Kryo kryo) { 18 | userSerializer.registerAllSerializers(kryo); 19 | } 20 | 21 | @Override 22 | void writeObject(Kryo kryo, Output output, T object) { 23 | kryo.writeClassAndObject(output, object); 24 | } 25 | 26 | @Override 27 | T readObject(Kryo kryo, Input input) { 28 | return (T) kryo.readClassAndObject(input); 29 | } 30 | 31 | @Override 32 | public int newId() { 33 | return IdGeneratorUtils.globalId(getHazelcastInstance()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoContext.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.InputChunked; 5 | import com.esotericsoftware.kryo.io.OutputChunked; 6 | 7 | final class KryoContext { 8 | private final Kryo kryo; 9 | private final InputChunked inputChunked; 10 | private final OutputChunked outputChunked; 11 | 12 | KryoContext(Kryo kryo, InputChunked inputChunked, OutputChunked outputChunked) { 13 | this.kryo = kryo; 14 | this.inputChunked = inputChunked; 15 | this.outputChunked = outputChunked; 16 | } 17 | 18 | public InputChunked getInputChunked() { 19 | return inputChunked; 20 | } 21 | 22 | public OutputChunked getOutputChunked() { 23 | return outputChunked; 24 | } 25 | 26 | public Kryo getKryo() { 27 | return kryo; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import com.esotericsoftware.kryo.ClassResolver; 4 | import com.esotericsoftware.kryo.Kryo; 5 | import com.esotericsoftware.kryo.ReferenceResolver; 6 | import com.esotericsoftware.kryo.io.Input; 7 | import com.esotericsoftware.kryo.io.InputChunked; 8 | import com.esotericsoftware.kryo.io.Output; 9 | import com.esotericsoftware.kryo.io.OutputChunked; 10 | import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; 11 | import com.hazelcast.core.HazelcastInstance; 12 | import info.jerrinot.subzero.internal.ClassLoaderUtils; 13 | import info.jerrinot.subzero.internal.IdGeneratorUtils; 14 | import org.objenesis.strategy.StdInstantiatorStrategy; 15 | 16 | import java.io.InputStream; 17 | import java.io.OutputStream; 18 | 19 | import static java.lang.Boolean.getBoolean; 20 | import static java.lang.Integer.getInteger; 21 | import static java.lang.String.format; 22 | import static java.lang.System.getProperty; 23 | 24 | public abstract class KryoStrategy { 25 | 26 | private static final int BUFFER_SIZE = getInteger("subzero.buffer.size.kb", 16) * 1024; 27 | private static final boolean IGNORE_HAZELCAST_CLASSLOADER = getBoolean("subzero.classloading.ignore"); 28 | 29 | private static final String DEFAULT_REFERENCE_RESOLVER_CLASS = "com.esotericsoftware.kryo.util.MapReferenceResolver"; 30 | private static final String REFERENCE_RESOLVER_CLASS_SYSTEM_PROPERTY = "subzero.referenceresolver.class"; 31 | 32 | private HazelcastInstance hazelcastInstance; 33 | 34 | private final ThreadLocal KRYOS = new ThreadLocal() { 35 | protected KryoContext initialValue() { 36 | Kryo kryo = newKryoInstance(); 37 | OutputChunked output = new OutputChunked(BUFFER_SIZE); 38 | InputChunked input = new InputChunked(BUFFER_SIZE); 39 | return new KryoContext(kryo, input, output); 40 | } 41 | }; 42 | 43 | private Kryo newKryoInstance() { 44 | Kryo kryo; 45 | if (IGNORE_HAZELCAST_CLASSLOADER) { 46 | kryo = new Kryo(createReferenceResolver()); 47 | kryo.setRegistrationRequired(false); 48 | } else { 49 | ClassLoader classLoader = ClassLoaderUtils.getConfiguredClassLoader(hazelcastInstance); 50 | ClassResolver classResolver = new DelegatingClassResolver(classLoader); 51 | ReferenceResolver referenceResolver = createReferenceResolver(); 52 | kryo = new Kryo(classResolver, referenceResolver); 53 | } 54 | registerCustomSerializers(kryo); 55 | kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); 56 | kryo.setRegistrationRequired(false); 57 | return kryo; 58 | } 59 | 60 | HazelcastInstance getHazelcastInstance() { 61 | return hazelcastInstance; 62 | } 63 | 64 | public abstract void registerCustomSerializers(Kryo kryo); 65 | 66 | public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { 67 | this.hazelcastInstance = hazelcastInstance; 68 | } 69 | 70 | public void write(OutputStream out, T object) { 71 | KryoContext kryoContext = KRYOS.get(); 72 | OutputChunked output = kryoContext.getOutputChunked(); 73 | output.setOutputStream(out); 74 | writeObject(kryoContext.getKryo(), output, object); 75 | output.endChunk(); 76 | output.flush(); 77 | } 78 | 79 | abstract void writeObject(Kryo kryo, Output output, T object); 80 | 81 | public T read(InputStream in) { 82 | KryoContext kryoContext = KRYOS.get(); 83 | InputChunked input = kryoContext.getInputChunked(); 84 | input.setInputStream(in); 85 | return readObject(kryoContext.getKryo(), input); 86 | } 87 | 88 | abstract T readObject(Kryo kryo, Input input); 89 | 90 | public void destroy(HazelcastInstance hazelcastInstance) { 91 | IdGeneratorUtils.instanceDestroyed(hazelcastInstance); 92 | } 93 | 94 | public abstract int newId(); 95 | 96 | private static ReferenceResolver createReferenceResolver() { 97 | String referenceResolverClass = getProperty(REFERENCE_RESOLVER_CLASS_SYSTEM_PROPERTY, DEFAULT_REFERENCE_RESOLVER_CLASS); 98 | try { 99 | Class resolverClass = Class.forName(referenceResolverClass); 100 | return (ReferenceResolver) resolverClass.getConstructor().newInstance(); 101 | 102 | } catch (Exception e) { 103 | throw new IllegalArgumentException(format("could not create ReferenceResolver with class %s", referenceResolverClass), e); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/TypedKryoStrategy.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import info.jerrinot.subzero.UserSerializer; 7 | import info.jerrinot.subzero.internal.IdGeneratorUtils; 8 | 9 | public class TypedKryoStrategy extends KryoStrategy { 10 | 11 | private final Class clazz; 12 | private final UserSerializer userSerializer; 13 | 14 | public TypedKryoStrategy(Class clazz, UserSerializer registrations) { 15 | this.clazz = clazz; 16 | this.userSerializer = registrations; 17 | } 18 | 19 | @Override 20 | public void registerCustomSerializers(Kryo kryo) { 21 | userSerializer.registerSingleSerializer(kryo, clazz); 22 | } 23 | 24 | @Override 25 | void writeObject(Kryo kryo, Output output, T object) { 26 | kryo.writeObject(output, object); 27 | } 28 | 29 | @Override 30 | T readObject(Kryo kryo, Input input) { 31 | return kryo.readObject(input, clazz); 32 | } 33 | 34 | @Override 35 | public int newId() { 36 | return IdGeneratorUtils.idForType(getHazelcastInstance(), clazz); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/ArraysAsListClassFactory.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import java.util.Arrays; 4 | 5 | public class ArraysAsListClassFactory implements ClassFactory { 6 | 7 | @Override 8 | public Class createClass() { 9 | return Arrays.asList( "" ).getClass(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/ClassLoadingTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.hazelcast.client.config.ClientConfig; 4 | import com.hazelcast.client.test.TestHazelcastFactory; 5 | import com.hazelcast.config.Config; 6 | import com.hazelcast.config.MapConfig; 7 | import com.hazelcast.core.HazelcastInstance; 8 | import com.hazelcast.core.IMap; 9 | import com.hazelcast.test.HazelcastTestSupport; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import java.io.Serializable; 14 | import java.net.URL; 15 | import java.net.URLClassLoader; 16 | 17 | import static com.hazelcast.config.InMemoryFormat.OBJECT; 18 | import static org.mockito.Mockito.spy; 19 | import static org.mockito.Mockito.verify; 20 | 21 | public class ClassLoadingTest extends HazelcastTestSupport { 22 | 23 | private TestHazelcastFactory hazelcastFactory; 24 | 25 | @Before 26 | public void setUp() { 27 | hazelcastFactory = new TestHazelcastFactory(); 28 | } 29 | 30 | @Test 31 | public void givenMemberHasClassLoaderConfigured_whenObjectIsStored_thenClassLoaderWillBeUsed() throws Exception { 32 | String mapName = randomMapName(); 33 | Config config = new Config(); 34 | SubZero.useAsGlobalSerializer(config); 35 | ClassLoader spyingClassLoader = createSpyingClassLoader(); 36 | config.setClassLoader(spyingClassLoader); 37 | config.addMapConfig(new MapConfig(mapName).setInMemoryFormat(OBJECT)); 38 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 39 | IMap myMap = member.getMap(mapName); 40 | 41 | myMap.put(0, new MyClass()); 42 | 43 | verify(spyingClassLoader).loadClass("info.jerrinot.subzero.ClassLoadingTest$MyClass"); 44 | } 45 | 46 | @Test 47 | public void givenClientHasClassLoaderConfigured_whenObjectIsFetched_thenClassLoaderWillBeUsed() throws Exception { 48 | Config memberConfig = new Config(); 49 | SubZero.useAsGlobalSerializer(memberConfig); 50 | hazelcastFactory.newHazelcastInstance(memberConfig); 51 | 52 | ClientConfig clientConfig = new ClientConfig(); 53 | ClassLoader clientClassLoader = createSpyingClassLoader(); 54 | clientConfig.setClassLoader(clientClassLoader); 55 | SubZero.useAsGlobalSerializer(clientConfig); 56 | HazelcastInstance client = hazelcastFactory.newHazelcastClient(clientConfig); 57 | IMap myMap = client.getMap(randomMapName()); 58 | myMap.put(0, new MyClass()); 59 | 60 | myMap.get(0); 61 | 62 | verify(clientClassLoader).loadClass("info.jerrinot.subzero.ClassLoadingTest$MyClass"); 63 | } 64 | 65 | 66 | private ClassLoader createSpyingClassLoader() { 67 | URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], this.getClass().getClassLoader()); 68 | return spy(urlClassLoader); 69 | } 70 | 71 | 72 | public static class MyClass implements Serializable { 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/SerializerTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.esotericsoftware.kryo.KryoException; 4 | import com.hazelcast.core.HazelcastInstance; 5 | import info.jerrinot.subzero.internal.PropertyUserSerializer; 6 | import info.jerrinot.subzero.test.TestUtils; 7 | import net.bytebuddy.ByteBuddy; 8 | import net.bytebuddy.ClassFileVersion; 9 | import net.bytebuddy.description.modifier.Visibility; 10 | import net.bytebuddy.dynamic.DynamicType; 11 | import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; 12 | import org.joda.time.LocalDate; 13 | import org.junit.After; 14 | import org.junit.Before; 15 | import org.junit.Rule; 16 | import org.junit.Test; 17 | 18 | import java.io.IOException; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import static info.jerrinot.subzero.SubzeroConfigRule.useConfig; 23 | import static info.jerrinot.subzero.test.TestUtils.newMockHazelcastInstance; 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertNotEquals; 26 | import static org.junit.Assert.assertTrue; 27 | 28 | public class SerializerTest { 29 | 30 | @Rule 31 | public SubzeroConfigRule configRule = useConfig("test-subzero-serializers.properties"); 32 | 33 | @Test 34 | public void givenSingleSerializerExist_whenHazelcastInstanceIsInjected_thenTypeIdIsGreaterThenZero() { 35 | Serializer serializer = new Serializer(); 36 | serializer.setHazelcastInstance(newMockHazelcastInstance()); 37 | 38 | HazelcastInstance hz = newMockHazelcastInstance(); 39 | serializer.setHazelcastInstance(hz); 40 | 41 | int typeId = serializer.getTypeId(); 42 | assertTrue(typeId > 0); 43 | } 44 | 45 | @Test 46 | public void givenTwoSerializerExist_whenDifferentHazelcastInstanceAreInjected_thenTheyHaveTheSameTypeId() { 47 | Serializer serializer1 = new Serializer(); 48 | serializer1.setHazelcastInstance(newMockHazelcastInstance()); 49 | Serializer serializer2 = new Serializer(); 50 | serializer2.setHazelcastInstance(newMockHazelcastInstance()); 51 | 52 | HazelcastInstance hz1 = newMockHazelcastInstance(); 53 | HazelcastInstance hz2 = newMockHazelcastInstance(); 54 | serializer1.setHazelcastInstance(hz1); 55 | serializer2.setHazelcastInstance(hz2); 56 | 57 | int typeId1 = serializer1.getTypeId(); 58 | int typeId2 = serializer2.getTypeId(); 59 | assertEquals(typeId1, typeId2); 60 | } 61 | 62 | @Test 63 | public void givenSpecificNeutralSerializerExist_whenObjectIsSerializedAndDeserialized_thenItHasTheSameInternalState() throws IOException { 64 | String input = "foo"; 65 | Serializer serializer = new Serializer(); 66 | serializer.setHazelcastInstance(newMockHazelcastInstance()); 67 | 68 | String output = TestUtils.serializeAndDeserializeObject(serializer, input); 69 | 70 | assertEquals(input, output); 71 | } 72 | 73 | @Test 74 | public void givenTwoTypeSpecificSerializersWithTheSameTypeExists_whenDifferentHazelcastInstancesAreInjected_thenTheyHaveTheSameTypeId() { 75 | Serializer serializer1 = new Serializer(String.class); 76 | serializer1.setHazelcastInstance(newMockHazelcastInstance()); 77 | 78 | Serializer serializer2 = new Serializer(String.class); 79 | serializer2.setHazelcastInstance(newMockHazelcastInstance()); 80 | 81 | HazelcastInstance hz1 = newMockHazelcastInstance(); 82 | HazelcastInstance hz2 = newMockHazelcastInstance(); 83 | serializer1.setHazelcastInstance(hz1); 84 | serializer2.setHazelcastInstance(hz2); 85 | 86 | int typeId1 = serializer1.getTypeId(); 87 | int typeId2 = serializer2.getTypeId(); 88 | assertEquals(typeId1, typeId2); 89 | } 90 | 91 | @Test 92 | public void givenTypeSpecificSerializerExist_whenObjectIsSerializedAndDeserialized_thenItHasTheSameInternalState() throws IOException { 93 | String input = "foo"; 94 | Serializer serializer = new Serializer(String.class); 95 | serializer.setHazelcastInstance(newMockHazelcastInstance()); 96 | 97 | String output = TestUtils.serializeAndDeserializeObject(serializer, input); 98 | 99 | assertEquals(input, output); 100 | } 101 | 102 | @Test 103 | public void givenJodaLocalDateIsRegisteredInProperties_whenLocalDateIsSerializedAndDeserialized_thenPrintlnDoesntThrowNPE() throws IOException { 104 | LocalDate input = LocalDate.now(); 105 | Serializer serializer = new Serializer(LocalDate.class); 106 | serializer.setHazelcastInstance(newMockHazelcastInstance()); 107 | 108 | LocalDate output = TestUtils.serializeAndDeserializeObject(serializer, input); 109 | 110 | //this throw an NPE when Joda LocalDate is not registered in Kryo 111 | System.out.println(output); 112 | } 113 | 114 | @Test(expected = KryoException.class) 115 | public void testAddedFields_defaultSerializer() throws Exception { 116 | testAddedFields(); 117 | } 118 | 119 | @Test 120 | public void testAddedFields_compatibilitySerializer() throws Exception { 121 | System.setProperty("subzero.custom.serializers.config.filename", "compatible-field-default-serializer.properties"); 122 | PropertyUserSerializer.reinitialize(); 123 | 124 | testAddedFields(); 125 | } 126 | 127 | private void testAddedFields() throws Exception { 128 | String classname = "some.pckage.SyntheticPerson"; 129 | String v1Field = "firstname"; 130 | String[] v2Fields = {v1Field, "lastname"}; 131 | String expectedFirstname = "somename"; 132 | 133 | //v1 class has just a single field: firstname 134 | ByteArrayClassLoader v1classLoader = TestUtils.createClass(classname, v1Field); 135 | Serializer serializerV1 = new Serializer((Class) v1classLoader.loadClass(classname)); 136 | serializerV1.setHazelcastInstance(newMockHazelcastInstance(v1classLoader)); 137 | 138 | //v2 class has two fields - the default Kryo serializer is not deserialize it 139 | ByteArrayClassLoader v2classLoader = TestUtils.createClass(classname, v2Fields); 140 | Serializer serializerV2 = new Serializer((Class) v2classLoader.loadClass(classname)); 141 | serializerV2.setHazelcastInstance(newMockHazelcastInstance(v2classLoader)); 142 | 143 | Object v1Instance = v1classLoader.loadClass(classname).newInstance(); 144 | v1Instance.getClass().getField(v1Field).set(v1Instance, expectedFirstname); 145 | 146 | byte[] blob = TestUtils.serialize(serializerV1, v1Instance); 147 | Object v2Instance = TestUtils.deserialize(serializerV2, blob); 148 | String actualFirstname = (String) v2Instance.getClass().getField(v1Field).get(v2Instance); 149 | 150 | assertEquals(expectedFirstname, actualFirstname); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/SubZeroTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.hazelcast.config.Config; 4 | import com.hazelcast.config.GlobalSerializerConfig; 5 | import com.hazelcast.config.SerializerConfig; 6 | import org.junit.Test; 7 | 8 | import java.util.Collection; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | import static org.junit.Assert.assertEquals; 12 | 13 | public class SubZeroTest { 14 | 15 | @Test 16 | public void givenGlobalSerializerConfigDoesNotExist_whenUseAsGlobalSerializer_thenNewSubZeroIsUsedAsGlobalSerializer() { 17 | Config config = new Config(); 18 | 19 | SubZero.useAsGlobalSerializer(config); 20 | 21 | assertEquals(Serializer.class.getName(), config.getSerializationConfig().getGlobalSerializerConfig().getClassName()); 22 | } 23 | 24 | @Test 25 | public void givenGlobalSerializerConfigDoes_whenUseAsGlobalSerializer_thenNewSubZeroIsUsedAsGlobalSerializer() { 26 | Config config = new Config(); 27 | config.getSerializationConfig().setGlobalSerializerConfig(new GlobalSerializerConfig().setClassName("foo")); 28 | 29 | SubZero.useAsGlobalSerializer(config); 30 | 31 | assertEquals(Serializer.class.getName(), config.getSerializationConfig().getGlobalSerializerConfig().getClassName()); 32 | } 33 | 34 | @Test 35 | public void useForClasses() { 36 | Config config = new Config(); 37 | SubZero.useForClasses(config, String.class); 38 | 39 | Collection serializerConfigs = config.getSerializationConfig().getSerializerConfigs(); 40 | assertThat(serializerConfigs) 41 | .extracting("typeClass") 42 | .contains(String.class); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/SubzeroConfigRule.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import info.jerrinot.subzero.internal.PropertyUserSerializer; 4 | import org.junit.rules.TestRule; 5 | import org.junit.runner.Description; 6 | import org.junit.runners.model.Statement; 7 | 8 | public class SubzeroConfigRule implements TestRule { 9 | private static final String CONFIG_FILE_PROP = "subzero.custom.serializers.config.filename"; 10 | 11 | private final String filename; 12 | 13 | private SubzeroConfigRule(String filename) { 14 | this.filename = filename; 15 | } 16 | 17 | public static SubzeroConfigRule useConfig(String filename) { 18 | return new SubzeroConfigRule(filename); 19 | } 20 | 21 | public SubzeroConfigRule reconfigure(String filename) { 22 | System.setProperty(CONFIG_FILE_PROP, filename); 23 | PropertyUserSerializer.reinitialize(); 24 | return this; 25 | } 26 | 27 | @Override 28 | public Statement apply(final Statement base, Description description) { 29 | return new Statement() { 30 | @Override 31 | public void evaluate() throws Throwable { 32 | String origValue = System.getProperty(CONFIG_FILE_PROP); 33 | System.setProperty(CONFIG_FILE_PROP, filename); 34 | PropertyUserSerializer.reinitialize(); 35 | try { 36 | base.evaluate(); 37 | } finally { 38 | if (origValue == null) { 39 | System.clearProperty(CONFIG_FILE_PROP); 40 | } else { 41 | System.setProperty(CONFIG_FILE_PROP, origValue); 42 | } 43 | } 44 | } 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/TestCustomerSerializers.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import com.hazelcast.client.config.ClientConfig; 7 | import com.hazelcast.client.test.TestHazelcastFactory; 8 | import com.hazelcast.config.Config; 9 | import com.hazelcast.config.SerializerConfig; 10 | import com.hazelcast.core.HazelcastInstance; 11 | import com.hazelcast.core.IMap; 12 | import com.hazelcast.test.HazelcastTestSupport; 13 | import info.jerrinot.subzero.test.AnotherNonSerializableObject; 14 | import info.jerrinot.subzero.test.NonSerializableObjectRegisteredInDefaultConfigFile; 15 | import org.junit.After; 16 | import org.junit.Before; 17 | import org.junit.Rule; 18 | import org.junit.Test; 19 | 20 | import java.util.Arrays; 21 | import java.util.Collections; 22 | import java.util.List; 23 | 24 | import static info.jerrinot.subzero.SubzeroConfigRule.useConfig; 25 | import static org.junit.Assert.assertEquals; 26 | 27 | 28 | public class TestCustomerSerializers extends HazelcastTestSupport { 29 | private TestHazelcastFactory hazelcastFactory; 30 | 31 | @Rule 32 | public SubzeroConfigRule configRule = useConfig("test-subzero-serializers.properties"); 33 | 34 | @Before 35 | public void setUp() { 36 | hazelcastFactory = new TestHazelcastFactory(); 37 | } 38 | 39 | @Test 40 | public void testGlobalCustomSerializerRegisteredInDefaultConfigFile() throws Exception { 41 | String mapName = randomMapName(); 42 | Config config = new Config(); 43 | SubZero.useAsGlobalSerializer(config); 44 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 45 | IMap myMap = member.getMap(mapName); 46 | 47 | myMap.put(0, new NonSerializableObjectRegisteredInDefaultConfigFile()); 48 | NonSerializableObjectRegisteredInDefaultConfigFile fromCache = myMap.get(0); 49 | 50 | assertEquals("deserialized", fromCache.name); 51 | } 52 | 53 | @Test 54 | public void testGlobalCustomSerializationConfiguredProgrammaticallyForHzConfig() { 55 | String mapName = randomMapName(); 56 | Config config = new Config(); 57 | 58 | SubZero.useAsGlobalSerializer(config, MyGlobalUserSerlizationConfig.class); 59 | 60 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 61 | IMap myMap = member.getMap(mapName); 62 | myMap.put(0, new AnotherNonSerializableObject()); 63 | AnotherNonSerializableObject fromCache = myMap.get(0); 64 | 65 | assertEquals("deserialized", fromCache.name); 66 | } 67 | 68 | @Test 69 | public void testGlobalCustomSerializationConfiguredProgrammaticallyForClientConfig() { 70 | Config memberConfig = new Config(); 71 | SubZero.useAsGlobalSerializer(memberConfig); 72 | hazelcastFactory.newHazelcastInstance(memberConfig); 73 | 74 | String mapName = randomMapName(); 75 | ClientConfig config = new ClientConfig(); 76 | 77 | SubZero.useAsGlobalSerializer(config, MyGlobalUserSerlizationConfig.class); 78 | 79 | HazelcastInstance member = hazelcastFactory.newHazelcastClient(config); 80 | IMap myMap = member.getMap(mapName); 81 | myMap.put(0, new AnotherNonSerializableObject()); 82 | AnotherNonSerializableObject fromCache = myMap.get(0); 83 | 84 | assertEquals("deserialized", fromCache.name); 85 | } 86 | 87 | @Test 88 | public void testGlobalCustomDelegateSerializationConfiguredProgrammaticallyForHzConfig() { 89 | String mapName = randomMapName(); 90 | Config config = new Config(); 91 | 92 | SubZero.useAsGlobalSerializer(config, MyGlobalDelegateSerlizationConfig.class); 93 | 94 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 95 | IMap myMap = member.getMap(mapName); 96 | myMap.put(0, new AnotherNonSerializableObject()); 97 | AnotherNonSerializableObject fromCache = myMap.get(0); 98 | 99 | assertEquals("deserialized", fromCache.name); 100 | } 101 | 102 | @Test 103 | public void testGlobalCustomDelegateSerializationConfiguredProgrammaticallyForClientConfig() { 104 | Config memberConfig = new Config(); 105 | SubZero.useAsGlobalSerializer(memberConfig); 106 | hazelcastFactory.newHazelcastInstance(memberConfig); 107 | 108 | String mapName = randomMapName(); 109 | ClientConfig config = new ClientConfig(); 110 | 111 | SubZero.useAsGlobalSerializer(config, MyGlobalDelegateSerlizationConfig.class); 112 | 113 | HazelcastInstance member = hazelcastFactory.newHazelcastClient(config); 114 | IMap myMap = member.getMap(mapName); 115 | myMap.put(0, new AnotherNonSerializableObject()); 116 | AnotherNonSerializableObject fromCache = myMap.get(0); 117 | 118 | assertEquals("deserialized", fromCache.name); 119 | } 120 | 121 | @Test 122 | public void testTypedCustomSerializerRegisteredInDefaultConfigFile() throws Exception { 123 | String mapName = randomMapName(); 124 | Config config = new Config(); 125 | SubZero.useForClasses(config, NonSerializableObjectRegisteredInDefaultConfigFile.class); 126 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 127 | IMap myMap = member.getMap(mapName); 128 | 129 | myMap.put(0, new NonSerializableObjectRegisteredInDefaultConfigFile()); 130 | NonSerializableObjectRegisteredInDefaultConfigFile fromCache = myMap.get(0); 131 | 132 | assertEquals("deserialized", fromCache.name); 133 | } 134 | 135 | @Test 136 | public void testGlobalCustomSerializer_SpecialRegistrationRegisteredInDefaultConfigFile() { 137 | String mapName = randomMapName(); 138 | Config config = new Config(); 139 | 140 | SubZero.useAsGlobalSerializer(config); 141 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 142 | IMap myMap = member.getMap(mapName); 143 | myMap.put(0, new ClassWithUnmodifieableList("foo")); 144 | 145 | //does not throw an exception 146 | myMap.get(0); 147 | } 148 | 149 | @Test 150 | public void testTypedSerializer_SpecialRegistrationRegisteredInDefaultConfigFile() { 151 | String mapName = randomMapName(); 152 | Config config = new Config(); 153 | 154 | SubZero.useForClasses(config, ClassWithUnmodifieableList.class); 155 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 156 | IMap myMap = member.getMap(mapName); 157 | myMap.put(0, new ClassWithUnmodifieableList("foo")); 158 | 159 | //does not throw an exception 160 | myMap.get(0); 161 | } 162 | 163 | @Test 164 | public void testTypedCustomSerializer_configuredBySubclassing() throws Exception { 165 | String mapName = randomMapName(); 166 | 167 | Config config = new Config(); 168 | SerializerConfig serializerConfig = new SerializerConfig(); 169 | serializerConfig.setClass(MySerializer.class); 170 | serializerConfig.setTypeClass(AnotherNonSerializableObject.class); 171 | config.getSerializationConfig().getSerializerConfigs().add(serializerConfig); 172 | 173 | HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); 174 | IMap myMap = member.getMap(mapName); 175 | 176 | myMap.put(0, new AnotherNonSerializableObject()); 177 | AnotherNonSerializableObject fromCache = myMap.get(0); 178 | 179 | assertEquals("deserialized", fromCache.name); 180 | } 181 | 182 | public static class MyGlobalUserSerlizationConfig extends AbstractGlobalUserSerializer { 183 | public MyGlobalUserSerlizationConfig() { 184 | super(UserSerializerConfig.register(AnotherNonSerializableObject.class, new AnotherNonSerializableObjectKryoSerializer())); 185 | } 186 | } 187 | 188 | public static class MyGlobalDelegateSerlizationConfig extends AbstractGlobalUserSerializer { 189 | public MyGlobalDelegateSerlizationConfig() { 190 | super(UserSerializerConfig.delegate(new KryoConfigurer() { 191 | @Override 192 | public void configure(Kryo kryo) { 193 | kryo.register(AnotherNonSerializableObject.class, new AnotherNonSerializableObjectKryoSerializer()); 194 | } 195 | })); 196 | } 197 | } 198 | 199 | public static final class ClassWithUnmodifieableList { 200 | private List names; 201 | 202 | public ClassWithUnmodifieableList(String...names) { 203 | this.names = Collections.unmodifiableList(Arrays.asList(names)); 204 | } 205 | } 206 | 207 | public static class MySerializer extends AbstractTypeSpecificUserSerializer { 208 | public MySerializer() { 209 | super(AnotherNonSerializableObject.class, new AnotherNonSerializableObjectKryoSerializer()); 210 | } 211 | } 212 | 213 | public static class AnotherNonSerializableObjectKryoSerializer extends com.esotericsoftware.kryo.Serializer { 214 | 215 | @Override 216 | public void write(Kryo kryo, Output output, AnotherNonSerializableObject object) { 217 | 218 | } 219 | 220 | @Override 221 | public AnotherNonSerializableObject read(Kryo kryo, Input input, Class type) { 222 | AnotherNonSerializableObject object = new AnotherNonSerializableObject(); 223 | object.name = "deserialized"; 224 | return object; 225 | } 226 | } 227 | 228 | } 229 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/example/HashMapSerializerExampleTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.example; 2 | 3 | import info.jerrinot.subzero.test.TestUtils; 4 | import org.junit.Test; 5 | 6 | import java.util.HashMap; 7 | 8 | import static info.jerrinot.subzero.test.TestUtils.newMockHazelcastInstance; 9 | import static org.junit.Assert.*; 10 | 11 | public class HashMapSerializerExampleTest { 12 | 13 | @Test 14 | public void testHashMapSerializer() throws Exception { 15 | HashMapSerializerExample serializer = new HashMapSerializerExample(); 16 | serializer.setHazelcastInstance(newMockHazelcastInstance()); 17 | 18 | HashMap inputMap = new HashMap(); 19 | inputMap.put(0, "Zero"); 20 | 21 | HashMap deserializedMap = TestUtils.serializeAndDeserializeObject(serializer, inputMap); 22 | assertEquals(inputMap, deserializedMap); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/GlobalKryoStrategyTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import info.jerrinot.subzero.internal.PropertyUserSerializer; 4 | import info.jerrinot.subzero.test.NonSerializableObject; 5 | import org.junit.Test; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | 11 | import static info.jerrinot.subzero.test.TestUtils.newMockHazelcastInstance; 12 | import static org.junit.Assert.assertEquals; 13 | 14 | public class GlobalKryoStrategyTest { 15 | 16 | @Test 17 | public void foo() throws IOException { 18 | NonSerializableObject joe = new NonSerializableObject("Joe"); 19 | GlobalKryoStrategy kryoStrategy = new GlobalKryoStrategy(PropertyUserSerializer.INSTANCE); 20 | kryoStrategy.setHazelcastInstance(newMockHazelcastInstance()); 21 | 22 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 23 | kryoStrategy.write(baos, joe); 24 | byte[] bytes = baos.toByteArray(); 25 | ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 26 | NonSerializableObject deserialized = (NonSerializableObject) kryoStrategy.read(bais); 27 | 28 | assertEquals(joe, deserialized); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/KryoStrategyCustomReferenceResolverTest.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.ReferenceResolver; 5 | import info.jerrinot.subzero.UserSerializer; 6 | import org.junit.Test; 7 | 8 | import java.lang.reflect.Field; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class KryoStrategyCustomReferenceResolverTest { 13 | 14 | @SuppressWarnings("unchecked") 15 | @Test 16 | public void should_use_strategy_with_specified_reference_resolver() throws Exception { 17 | // given 18 | System.setProperty("subzero.referenceresolver.class", "info.jerrinot.subzero.internal.strategy.NullReferenceResolver"); 19 | GlobalKryoStrategy kryoStrategy = new GlobalKryoStrategy(NULL_USER_SERIALIZER); 20 | 21 | // when 22 | Field field = KryoStrategy.class.getDeclaredField("KRYOS"); 23 | field.setAccessible(true); 24 | Class actualClassResolver = ((ThreadLocal) field.get(kryoStrategy)) 25 | .get().getKryo().getReferenceResolver().getClass(); 26 | 27 | // then 28 | assertEquals(NullReferenceResolver.class, actualClassResolver); 29 | } 30 | 31 | private static final UserSerializer NULL_USER_SERIALIZER = new UserSerializer() { 32 | @Override 33 | public void registerSingleSerializer(Kryo kryo, Class clazz) { 34 | // do nothing 35 | } 36 | @Override 37 | public void registerAllSerializers(Kryo kryo) { 38 | // do nothing 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/NullReferenceResolver.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.internal.strategy; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.ReferenceResolver; 5 | 6 | public class NullReferenceResolver implements ReferenceResolver { 7 | @Override 8 | public void setKryo(Kryo kryo) { 9 | // do nothing 10 | } 11 | 12 | @Override 13 | public int getWrittenId(Object object) { 14 | return 0; 15 | } 16 | 17 | @Override 18 | public int addWrittenObject(Object object) { 19 | return 0; 20 | } 21 | 22 | @Override 23 | public int nextReadId(Class type) { 24 | return 0; 25 | } 26 | 27 | @Override 28 | public void setReadObject(int id, Object object) { 29 | // do nothing 30 | } 31 | 32 | @Override 33 | public Object getReadObject(Class type, int id) { 34 | return null; 35 | } 36 | 37 | @Override 38 | public void reset() { 39 | // do nothing 40 | } 41 | 42 | @Override 43 | public boolean useReferences(Class type) { 44 | return false; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/test/AnotherNonSerializableObject.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.test; 2 | 3 | public class AnotherNonSerializableObject { 4 | public String name = "default"; 5 | } 6 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/test/MutatingObjectSerializer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.test; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.Serializer; 5 | import com.esotericsoftware.kryo.io.Input; 6 | import com.esotericsoftware.kryo.io.Output; 7 | 8 | public class MutatingObjectSerializer extends Serializer { 9 | 10 | @Override 11 | public void write(Kryo kryo, Output output, NonSerializableObjectRegisteredInDefaultConfigFile object) { 12 | 13 | } 14 | 15 | @Override 16 | public NonSerializableObjectRegisteredInDefaultConfigFile read(Kryo kryo, Input input, Class type) { 17 | NonSerializableObjectRegisteredInDefaultConfigFile object = new NonSerializableObjectRegisteredInDefaultConfigFile(); 18 | object.name = "deserialized"; 19 | return object; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/test/NonSerializableObject.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.test; 2 | 3 | public final class NonSerializableObject { 4 | private String name; 5 | 6 | public NonSerializableObject(String name) { 7 | this.name = name; 8 | } 9 | 10 | public NonSerializableObject() { } 11 | 12 | public String getName() { 13 | return name; 14 | } 15 | 16 | @Override 17 | public boolean equals(Object o) { 18 | if (this == o) return true; 19 | if (!(o instanceof NonSerializableObject)) return false; 20 | 21 | NonSerializableObject nonSerializableObject = (NonSerializableObject) o; 22 | 23 | return name.equals(nonSerializableObject.name); 24 | 25 | } 26 | 27 | @Override 28 | public int hashCode() { 29 | return name.hashCode(); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "NonSerializableObject{" + 35 | "name='" + name + '\'' + 36 | '}'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/test/NonSerializableObjectRegisteredInDefaultConfigFile.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.test; 2 | 3 | public class NonSerializableObjectRegisteredInDefaultConfigFile { 4 | public String name = "default"; 5 | } 6 | -------------------------------------------------------------------------------- /subzero-core/src/test/java/info/jerrinot/subzero/test/TestUtils.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.test; 2 | 3 | import com.hazelcast.config.Config; 4 | import com.hazelcast.core.HazelcastInstance; 5 | import com.hazelcast.internal.serialization.InternalSerializationService; 6 | import com.hazelcast.internal.serialization.impl.ObjectDataInputStream; 7 | import com.hazelcast.internal.serialization.impl.ObjectDataOutputStream; 8 | import com.hazelcast.nio.serialization.StreamSerializer; 9 | import info.jerrinot.subzero.SerializerTest; 10 | import net.bytebuddy.ByteBuddy; 11 | import net.bytebuddy.ClassFileVersion; 12 | import net.bytebuddy.description.modifier.Visibility; 13 | import net.bytebuddy.dynamic.DynamicType; 14 | import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; 15 | 16 | import java.io.ByteArrayInputStream; 17 | import java.io.ByteArrayOutputStream; 18 | import java.io.IOException; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import static org.mockito.Mockito.*; 23 | 24 | public class TestUtils { 25 | 26 | public static T serializeAndDeserializeObject(StreamSerializer serializer, T input) throws IOException { 27 | byte[] blob = serialize(serializer, input); 28 | return deserialize(serializer, blob); 29 | } 30 | 31 | public static byte[] serialize(StreamSerializer serializer, T input) throws IOException{ 32 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 33 | InternalSerializationService mockSerializationService = mock(InternalSerializationService.class, withSettings().stubOnly()); 34 | ObjectDataOutputStream odos = new ObjectDataOutputStream(os, mockSerializationService); 35 | serializer.write(odos, input); 36 | return os.toByteArray(); 37 | } 38 | 39 | public static T deserialize(StreamSerializer serializer, byte[] blob) throws IOException { 40 | InternalSerializationService mockSerializationService = mock(InternalSerializationService.class, withSettings().stubOnly()); 41 | ObjectDataInputStream odis = new ObjectDataInputStream(new ByteArrayInputStream(blob), mockSerializationService); 42 | return serializer.read(odis); 43 | } 44 | 45 | public static HazelcastInstance newMockHazelcastInstance() { 46 | return newMockHazelcastInstance(null); 47 | } 48 | 49 | public static HazelcastInstance newMockHazelcastInstance(ClassLoader classLoader) { 50 | HazelcastInstance hz = mock(HazelcastInstance.class); 51 | Config config = new Config(); 52 | config.setClassLoader(classLoader); 53 | when(hz.getConfig()).thenReturn(config); 54 | return hz; 55 | } 56 | 57 | public static ByteArrayClassLoader createClass(String classname, String...fields) { 58 | DynamicType.Builder byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V6) 59 | .subclass(Object.class) 60 | .name(classname) 61 | .modifiers(Visibility.PUBLIC); 62 | for (String field : fields) { 63 | byteBuddy = byteBuddy.defineField(field, String.class, Visibility.PUBLIC); 64 | } 65 | byte[] bytes = byteBuddy.make().getBytes(); 66 | Map typeDefinitions = new HashMap(); 67 | typeDefinitions.put(classname, bytes); 68 | return new ByteArrayClassLoader(SerializerTest.class.getClassLoader(), typeDefinitions); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /subzero-core/src/test/resources/compatible-field-default-serializer.properties: -------------------------------------------------------------------------------- 1 | defaultSerializer=CompatibleFieldSerializer -------------------------------------------------------------------------------- /subzero-core/src/test/resources/test-subzero-serializers.properties: -------------------------------------------------------------------------------- 1 | info.jerrinot.subzero.test.NonSerializableObjectRegisteredInDefaultConfigFile=info.jerrinot.subzero.test.MutatingObjectSerializer 2 | UnmodifiableCollectionsSerializer 3 | ArraysAsListSerializer 4 | org.joda.time.LocalDate=de.javakaffee.kryoserializers.jodatime.JodaLocalDateSerializer -------------------------------------------------------------------------------- /subzero-it-base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | subzero 5 | info.jerrinot 6 | 0.12-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | subzero-it-base 11 | 12 | 13 | 14 | info.jerrinot 15 | subzero-all 16 | ${project.version} 17 | 18 | 19 | 20 | com.hazelcast 21 | hazelcast 22 | ${hazelcast.version} 23 | 24 | 25 | 26 | com.hazelcast 27 | hazelcast-client 28 | ${hazelcast.version} 29 | 30 | 31 | 32 | com.hazelcast 33 | hazelcast 34 | ${hazelcast.version} 35 | tests 36 | 37 | 38 | 39 | com.hazelcast 40 | hazelcast-client 41 | ${hazelcast.version} 42 | tests 43 | 44 | 45 | 46 | junit 47 | junit 48 | ${junit.version} 49 | 50 | 51 | 52 | log4j 53 | log4j 54 | ${log4j.version} 55 | 56 | 57 | 58 | net.bytebuddy 59 | byte-buddy 60 | ${bytebuddy.version} 61 | 62 | 63 | 64 | info.jerrinot 65 | subzero-core 66 | ${project.version} 67 | tests 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-surefire-plugin 76 | 2.4.2 77 | 78 | true 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /subzero-it-base/src/main/java/info/jerrinot/subzero/it/BaseSmokeTests.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it; 2 | 3 | import com.hazelcast.client.config.ClientConfig; 4 | import com.hazelcast.client.test.TestHazelcastFactory; 5 | import com.hazelcast.config.Config; 6 | import com.hazelcast.config.GlobalSerializerConfig; 7 | import com.hazelcast.config.SerializationConfig; 8 | import com.hazelcast.config.SerializerConfig; 9 | import com.hazelcast.core.HazelcastInstance; 10 | import com.hazelcast.nio.serialization.HazelcastSerializationException; 11 | import com.hazelcast.test.HazelcastTestSupport; 12 | import info.jerrinot.subzero.Serializer; 13 | import info.jerrinot.subzero.SubZero; 14 | import info.jerrinot.subzero.SubzeroConfigRule; 15 | import info.jerrinot.subzero.internal.ClassLoaderUtils; 16 | import info.jerrinot.subzero.test.TestUtils; 17 | import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; 18 | import org.junit.After; 19 | import org.junit.Rule; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.junit.runners.Parameterized; 23 | 24 | import java.util.Arrays; 25 | import java.util.Collection; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | import static com.hazelcast.config.InMemoryFormat.OBJECT; 29 | import static info.jerrinot.subzero.SubZero.useAsGlobalSerializer; 30 | import static info.jerrinot.subzero.SubZero.useForClasses; 31 | import static info.jerrinot.subzero.SubzeroConfigRule.useConfig; 32 | import static info.jerrinot.subzero.it.MapProxy.newProxy; 33 | import static org.junit.Assert.assertEquals; 34 | 35 | @RunWith(Parameterized.class) 36 | public abstract class BaseSmokeTests extends HazelcastTestSupport { 37 | private static final int CLUSTER_SIZE = 2; 38 | 39 | private TestHazelcastFactory factory = new TestHazelcastFactory(); 40 | 41 | @Parameterized.Parameter(0) 42 | public boolean useClient; 43 | 44 | @Parameterized.Parameters(name = "useClient:{0}") 45 | public static Collection parameters() { 46 | return Arrays.asList(new Object[][]{ 47 | {false}, 48 | {true}, 49 | }); 50 | } 51 | 52 | @Rule 53 | public SubzeroConfigRule configRule = useConfig("subzero-serializers.properties"); 54 | 55 | @After 56 | public void tearDown() { 57 | factory.shutdownAll(); 58 | } 59 | 60 | @Test(expected = HazelcastSerializationException.class) 61 | public void testInfrastructure() { 62 | //make sure the test fails when Subzero is not enabled 63 | 64 | HazelcastInstance[] instances = createInstances(new Configurer() { 65 | @Override 66 | public void configure(Object o) { 67 | //intentionally empty 68 | } 69 | }); 70 | executeTest(instances); 71 | } 72 | 73 | @Test 74 | public void testGlobalSerializer() { 75 | HazelcastInstance[] instances = createInstances(new Configurer() { 76 | @Override 77 | public void configure(Object configurationObject) { 78 | SerializationConfig serializationConfig = extractSerializationConfig(configurationObject); 79 | configureGlobalConfig(serializationConfig); 80 | } 81 | }); 82 | executeTest(instances); 83 | } 84 | 85 | @Test 86 | public void testTypedSerializer() { 87 | HazelcastInstance[] instances = createInstances(new Configurer() { 88 | @Override 89 | public void configure(Object configurationObject) { 90 | SerializationConfig serializationConfig = extractSerializationConfig(configurationObject); 91 | configureTypedConfig(serializationConfig); 92 | } 93 | }); 94 | executeTest(instances); 95 | } 96 | 97 | @Test 98 | public void testTypedSerializer_withInjector() { 99 | HazelcastInstance[] instances = createInstances(new Configurer() { 100 | @Override 101 | public void configure(Object configurationObject) { 102 | useForClasses(configurationObject, Person.class); 103 | } 104 | }); 105 | executeTest(instances); 106 | } 107 | 108 | @Test 109 | public void testGlobalSerializer_withInjector() { 110 | HazelcastInstance[] instances = createInstances(new Configurer() { 111 | @Override 112 | public void configure(Object configurationObject) { 113 | useAsGlobalSerializer(configurationObject); 114 | } 115 | }); 116 | 117 | executeTest(instances); 118 | } 119 | 120 | @Test 121 | public void testCompat() throws Exception { 122 | configRule.reconfigure("compatible-field-default-serializer.properties"); 123 | 124 | final String classname = "some.pckage.SyntheticPerson"; 125 | final String constantField = "firstname"; 126 | String expectedFirstname = "somename"; 127 | String mapName = randomMapName(); 128 | 129 | final AtomicInteger instanceCounter = new AtomicInteger(); 130 | HazelcastInstance[] instances = createInstances(new Configurer() { 131 | @Override 132 | public void configure(Object configurationObject) { 133 | int extraFieldCount = instanceCounter.incrementAndGet(); 134 | int totalFieldCount = 1 + extraFieldCount; 135 | String fields[] = new String[totalFieldCount]; 136 | fields[0] = constantField; 137 | for (int i = 1; i < fields.length; i++) { 138 | fields[i] = "extrafield" + i; 139 | } 140 | ByteArrayClassLoader classLoader = TestUtils.createClass(classname, fields); 141 | 142 | if (configurationObject instanceof Config) { 143 | Config config = (Config) configurationObject; 144 | config.getMapConfig("default").setInMemoryFormat(OBJECT); 145 | config.setClassLoader(classLoader); 146 | } else if (configurationObject instanceof ClientConfig) { 147 | ((ClientConfig) configurationObject).setClassLoader(classLoader); 148 | } 149 | SubZero.useAsGlobalSerializer(configurationObject); 150 | } 151 | }); 152 | 153 | HazelcastInstance i0 = instances[0]; 154 | MapProxy map0 = newProxy(i0, mapName); 155 | ClassLoader cl0 = ClassLoaderUtils.getConfiguredClassLoader(i0); 156 | Object o1 = cl0.loadClass(classname).newInstance(); 157 | o1.getClass().getField(constantField).set(o1, expectedFirstname); 158 | map0.set(0, o1); 159 | 160 | HazelcastInstance i1 = instances[1]; 161 | MapProxy map1 = newProxy(i1, mapName); 162 | Object o = map1.get(0); 163 | 164 | String actualFirstname = (String) o.getClass().getField(constantField).get(o); 165 | assertEquals(expectedFirstname, actualFirstname); 166 | } 167 | 168 | private void executeTest(HazelcastInstance[] instances) { 169 | MapProxy map = newProxy(instances[0], "myMap"); 170 | Person joe = new Person("Joe"); 171 | map.set(0, joe); 172 | 173 | assertEquals(joe, map.get(0)); 174 | } 175 | 176 | private static SerializationConfig extractSerializationConfig(Object configurationObject) { 177 | if (configurationObject instanceof Config) { 178 | return ((Config) configurationObject).getSerializationConfig(); 179 | } else if (configurationObject instanceof ClientConfig) { 180 | return ((ClientConfig) configurationObject).getSerializationConfig(); 181 | } else { 182 | throw new AssertionError("unknown configuration object " + configurationObject); 183 | } 184 | } 185 | 186 | private HazelcastInstance[] createInstances(Configurer configurer) { 187 | int totalInstanceCount = useClient ? CLUSTER_SIZE + 1 : CLUSTER_SIZE; 188 | HazelcastInstance[] instances = new HazelcastInstance[totalInstanceCount]; 189 | 190 | for (int i = useClient ? 1 : 0; i < totalInstanceCount; i++) { 191 | Config config = new Config(); 192 | configurer.configure(config); 193 | instances[i] = factory.newHazelcastInstance(config); 194 | } 195 | if (useClient) { 196 | ClientConfig config = new ClientConfig(); 197 | configurer.configure(config); 198 | instances[0] = factory.newHazelcastClient(config); 199 | } 200 | return instances; 201 | } 202 | 203 | private void configureTypedConfig(SerializationConfig config) { 204 | SerializerConfig serializerConfig = new SerializerConfig() 205 | .setTypeClassName("info.jerrinot.subzero.it.Person") 206 | .setClassName("info.jerrinot.subzero.Serializer"); 207 | 208 | config.addSerializerConfig(serializerConfig); 209 | } 210 | 211 | private void configureGlobalConfig(SerializationConfig config) { 212 | GlobalSerializerConfig globalSerializerConfig = new GlobalSerializerConfig() 213 | .setClassName(Serializer.class.getName()) 214 | .setOverrideJavaSerialization(true); 215 | config.setGlobalSerializerConfig(globalSerializerConfig); 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /subzero-it-base/src/main/java/info/jerrinot/subzero/it/Configurer.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it; 2 | 3 | public interface Configurer { 4 | void configure(Object configObject); 5 | } 6 | -------------------------------------------------------------------------------- /subzero-it-base/src/main/java/info/jerrinot/subzero/it/MapProxy.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it; 2 | 3 | import com.hazelcast.core.HazelcastInstance; 4 | 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * Abstract away different IMap package between Hazelcast 3.x and Hazelcast 4.x 10 | * 11 | * @param 12 | * @param 13 | */ 14 | public final class MapProxy { 15 | 16 | private final Object imapObject; 17 | 18 | private MapProxy(Object imapObject) { 19 | this.imapObject = imapObject; 20 | } 21 | 22 | private static final Method GET_MAP_PROXY_METHOD; 23 | private static final Method SET_KEY_METHOD; 24 | private static final Method GET_KEY_METHOD; 25 | 26 | static { 27 | try { 28 | GET_MAP_PROXY_METHOD = HazelcastInstance.class.getMethod("getMap", String.class); 29 | Class imapClass = GET_MAP_PROXY_METHOD.getReturnType(); 30 | SET_KEY_METHOD = imapClass.getMethod("set", Object.class, Object.class); 31 | GET_KEY_METHOD = imapClass.getMethod("get", Object.class); 32 | } catch (NoSuchMethodException e) { 33 | throw new AssertionError("Hazelcast instance does not have getMap()"); 34 | } 35 | } 36 | 37 | /** 38 | * Create new IMap proxy 39 | * 40 | * @param instance hazelcast instance to use to get the proxy 41 | * @param mapName name of the map 42 | * @param map Key 43 | * @param map Value 44 | * @return new Map proxy to access IMap 45 | */ 46 | public static MapProxy newProxy(HazelcastInstance instance, String mapName) { 47 | Object imap = invokeReflectively(GET_MAP_PROXY_METHOD, instance, mapName); 48 | return new MapProxy(imap); 49 | } 50 | 51 | public void set(K k, V v) { 52 | invokeReflectively(SET_KEY_METHOD, imapObject, k, v); 53 | } 54 | 55 | public V get(K k) { 56 | return (V) invokeReflectively(GET_KEY_METHOD, imapObject, k); 57 | } 58 | 59 | private static Object invokeReflectively(Method method, Object object, Object... args) { 60 | try { 61 | return method.invoke(object, args); 62 | } catch (IllegalAccessException e) { 63 | throw new IllegalStateException("Error while invoking method reflectively", e); 64 | } catch (InvocationTargetException e) { 65 | Throwable cause = e.getCause(); 66 | if (cause == null) { 67 | cause = e; 68 | } 69 | if (cause instanceof RuntimeException) { 70 | throw (RuntimeException)cause; 71 | } else throw new IllegalStateException("Error while invoking method reflectively", cause); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /subzero-it-base/src/main/java/info/jerrinot/subzero/it/Person.java: -------------------------------------------------------------------------------- 1 | package info.jerrinot.subzero.it; 2 | 3 | public final class Person { 4 | private String name; 5 | 6 | public Person(String name) { 7 | this.name = name; 8 | } 9 | 10 | public Person() { } 11 | 12 | public String getName() { 13 | return name; 14 | } 15 | 16 | @Override 17 | public boolean equals(Object o) { 18 | if (this == o) return true; 19 | if (!(o instanceof Person)) return false; 20 | 21 | Person person = (Person) o; 22 | 23 | return name.equals(person.name); 24 | 25 | } 26 | 27 | @Override 28 | public int hashCode() { 29 | return name.hashCode(); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Person{" + 35 | "name='" + name + '\'' + 36 | '}'; 37 | } 38 | } 39 | --------------------------------------------------------------------------------