├── .gitignore ├── .travis.yml ├── LICENSE ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── java │ └── cn │ └── teaey │ └── apns4j │ ├── Apns4j.java │ ├── ApnsException.java │ ├── ApnsHelper.java │ ├── keystore │ ├── InvalidKeyStoreException.java │ ├── InvalidKeyStoreFormatException.java │ ├── InvalidKeyStorePasswordException.java │ ├── InvalidKeyStoreTypeException.java │ ├── KeyStoreGetter.java │ └── KeyStoreType.java │ ├── network │ ├── ApnsChannel.java │ ├── ApnsChannelFactory.java │ ├── ApnsGateway.java │ ├── Channel.java │ ├── SecuritySocketFactory.java │ └── async │ │ ├── ApnsFuture.java │ │ ├── ApnsService.java │ │ ├── AsyncServiceShutdownException.java │ │ └── PayloadSender.java │ └── protocol │ ├── ApnsPayload.java │ ├── ErrorResp.java │ ├── InvalidDeviceTokenException.java │ ├── InvalidErrorResponse.java │ ├── JsonParser.java │ ├── ListMap.java │ ├── Payload.java │ └── Protocal.java └── test └── java └── cn └── teaey └── apns4j ├── ActionButtonTest.java ├── AlertTest.java ├── BadgeTest.java ├── MainTest.java └── TestConts.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | .idea 8 | .idea/** 9 | *.iml 10 | *.jar 11 | target 12 | target/** 13 | *.ipr 14 | *.iws 15 | *.p12 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apns4j 2 | 3 | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 4 | [![Build Status](https://travis-ci.org/teaey/apns4j.svg?branch=master)](https://travis-ci.org/teaey/apns4j) 5 | 6 | ### Quick start 7 | 8 | ``` 9 | 10 | cn.teaey.apns4j 11 | apns4j 12 | 1.1.4 13 | 14 | ``` 15 | 16 | ``` 17 | //Step 1 18 | ApnsChannelFactory apnsChannelFactory = Apns4j.newChannelFactoryBuilder() 19 | .keyStoreMeta("${path to your keystore}") 20 | .keyStorePwd("${keystore password}") 21 | .build(); 22 | 23 | //Setp 2 24 | ApnsChannel apnsChannel = apnsChannelFactory.newChannel(); 25 | 26 | //Step 3 create & init notify payload 27 | ApnsPayload apnsPayload = Apns4j.newPayload() 28 | .alertTitle("Title") 29 | .alertBody("Pushed by apns4j") 30 | .sound("default"); 31 | 32 | //Step 4 send via channel 33 | apnsChannel.send("${target device token}", apnsPayload); 34 | 35 | //Step 5 in the end, apnsChannel can be Recycle and Reuse 36 | apnsChannel.close(); 37 | ``` 38 | 39 | 40 | #### Version Control 41 | 42 | ``` 43 | This project uses Semantic Versioning. Version format is X.Y.Z: 44 | 45 | X: New program version. 46 | 47 | Y: New feature or huge bug fix patch. 48 | 49 | Z: Minor fix or patch. 50 | ``` 51 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | cn.teaey 9 | teaey-parent 10 | 1.0.1 11 | 12 | 13 | cn.teaey.apns4j 14 | apns4j 15 | 1.1.4 16 | jar 17 | https://github.com/teaey/apns4j.git 18 | Apns4j 19 | 20 | 21 | 1.7 22 | 23 | 24 | 25 | 26 | Teaey(Xiaofei.Wu) 27 | masfay@163.com 28 | teaey.github.com 29 | +8 30 | 31 | 32 | 33 | 34 | 35 | junit 36 | junit 37 | 4.13.1 38 | test 39 | 40 | 41 | com.github.teaey 42 | feva-all 43 | 1.0.0 44 | test 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-compiler-plugin 52 | ${version.compiler-plugin} 53 | 54 | ${java.version} 55 | ${java.version} 56 | ${java.version} 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/Apns4j.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j; 20 | 21 | import cn.teaey.apns4j.keystore.KeyStoreGetter; 22 | import cn.teaey.apns4j.keystore.KeyStoreType; 23 | import cn.teaey.apns4j.network.ApnsChannelFactory; 24 | import cn.teaey.apns4j.network.ApnsGateway; 25 | import cn.teaey.apns4j.network.SecuritySocketFactory; 26 | import cn.teaey.apns4j.protocol.ApnsPayload; 27 | 28 | import java.io.File; 29 | import java.io.InputStream; 30 | 31 | /** 32 | * @author xiaofei.wxf 33 | * @since 1.0.2 34 | */ 35 | public class Apns4j { 36 | /** 37 | *

newNotifyPayload.

38 | * 39 | * @return a {@link ApnsPayload} object. 40 | */ 41 | public static final ApnsPayload newPayload() { 42 | return new ApnsPayload(); 43 | } 44 | 45 | public static final ApnsChannelFactoryBuilder newChannelFactoryBuilder() { 46 | return new ApnsChannelFactoryBuilder(); 47 | } 48 | 49 | public static final class ApnsChannelFactoryBuilder { 50 | private Object keyStoreMeta; 51 | private String keyStorePwd; 52 | private ApnsGateway apnsGateway = ApnsGateway.DEVELOPMENT; 53 | private KeyStoreType keyStoreType = KeyStoreType.PKCS12; 54 | 55 | public ApnsChannelFactoryBuilder keyStoreMeta(Object keyStoreMeta) { 56 | ApnsHelper.checkNullThrowException(keyStoreMeta, "keyStoreMeta"); 57 | this.keyStoreMeta = keyStoreMeta; 58 | return this; 59 | } 60 | 61 | public ApnsChannelFactoryBuilder keyStorePwd(String keyStorePwd) { 62 | ApnsHelper.checkNullThrowException(keyStorePwd, "keyStorePwd"); 63 | this.keyStorePwd = keyStorePwd; 64 | return this; 65 | } 66 | 67 | public ApnsChannelFactoryBuilder apnsGateway(ApnsGateway apnsGateway) { 68 | ApnsHelper.checkNullThrowException(apnsGateway, "apnsGateway"); 69 | this.apnsGateway = apnsGateway; 70 | return this; 71 | } 72 | 73 | public ApnsChannelFactoryBuilder keyStoreType(KeyStoreType keyStoreType) { 74 | ApnsHelper.checkNullThrowException(keyStoreType, "keyStoreType"); 75 | this.keyStoreType = keyStoreType; 76 | return this; 77 | } 78 | 79 | public ApnsChannelFactory build() { 80 | KeyStoreGetter keyStoreGetter = null; 81 | if (keyStoreMeta instanceof String) { 82 | keyStoreGetter = new KeyStoreGetter((String) this.keyStoreMeta, this.keyStorePwd, this.keyStoreType); 83 | } else if (keyStoreMeta instanceof File) { 84 | keyStoreGetter = new KeyStoreGetter((File) this.keyStoreMeta, this.keyStorePwd, this.keyStoreType); 85 | } else if (keyStoreMeta instanceof InputStream) { 86 | keyStoreGetter = new KeyStoreGetter((InputStream) this.keyStoreMeta, this.keyStorePwd, this.keyStoreType); 87 | } 88 | if (null == keyStoreGetter) { 89 | throw new IllegalArgumentException(keyStoreMeta.getClass().getName()); 90 | } 91 | SecuritySocketFactory securitySocketFactory = new SecuritySocketFactory(this.apnsGateway.host(), this.apnsGateway.port(), keyStoreGetter.keyStore(), keyStoreGetter.keyStorePwd()); 92 | return new ApnsChannelFactory(securitySocketFactory); 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/ApnsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j; 20 | 21 | /** 22 | * @author xiaofei.wxf 23 | */ 24 | public class ApnsException extends RuntimeException { 25 | public ApnsException() { 26 | } 27 | 28 | public ApnsException(String message) { 29 | super(message); 30 | } 31 | 32 | public ApnsException(String message, Throwable cause) { 33 | super(message, cause); 34 | } 35 | 36 | public ApnsException(Throwable cause) { 37 | super(cause); 38 | } 39 | 40 | public ApnsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 41 | super(message, cause, enableSuppression, writableStackTrace); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/ApnsHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j; 20 | 21 | import cn.teaey.apns4j.protocol.InvalidDeviceTokenException; 22 | import cn.teaey.apns4j.protocol.Protocal; 23 | 24 | import java.nio.ByteBuffer; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | /** 28 | * @author teaey 29 | * @since 1.0.0 30 | */ 31 | public class ApnsHelper { 32 | private static final int HEADER_LENGTH = 45; 33 | 34 | public static final AtomicInteger IDENTIFIER = new AtomicInteger(0); 35 | 36 | /** 37 | *

toRequestBytes.

38 | * 39 | * @param deviceTokenBytes an array of byte. 40 | * @param payloadJsonString a {@link String} object. 41 | * @param identifier a int. 42 | * @param expiry a int. 43 | * @return an array of byte. 44 | */ 45 | public static final byte[] toRequestBytes(byte[] deviceTokenBytes, String payloadJsonString, int identifier, int expiry) { 46 | byte[] payloadBytes = payloadJsonString.getBytes(Protocal.DEF_CHARSET); 47 | ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + payloadBytes.length); 48 | buffer.put((byte) 1); 49 | buffer.putInt(identifier); 50 | buffer.putInt(expiry); 51 | buffer.putShort((short) 32); 52 | buffer.put(deviceTokenBytes); 53 | buffer.putShort((short) payloadBytes.length); 54 | buffer.put(payloadBytes); 55 | return buffer.array(); 56 | } 57 | 58 | /** 59 | *

toHexString.

60 | * 61 | * @param bytes an array of byte. 62 | * @return a {@link String} object. 63 | */ 64 | public static final String toHexString(byte[] bytes) { 65 | StringBuilder ret = new StringBuilder(64); 66 | for (byte each : bytes) { 67 | String hexStr = Integer.toHexString(each >= 0 ? each : (256 + each)); 68 | if (hexStr.length() == 1) { 69 | ret.append("0"); 70 | } 71 | ret.append(hexStr); 72 | } 73 | return ret.toString(); 74 | } 75 | 76 | /** 77 | *

toByteArray.

78 | * 79 | * @param deviceToken a {@link String} object. 80 | * @return an array of byte. 81 | */ 82 | public static final byte[] toByteArray(String deviceToken) { 83 | byte[] deviceTokenAsBytes = new byte[deviceToken.length() / 2]; 84 | deviceToken = deviceToken.toUpperCase(); 85 | int j = 0; 86 | try { 87 | for (int i = 0; i < deviceToken.length(); i += 2) { 88 | String t = deviceToken.substring(i, i + 2); 89 | deviceTokenAsBytes[j++] = (byte) Integer.parseInt(t, 16); 90 | } 91 | } catch (NumberFormatException e) { 92 | throw new InvalidDeviceTokenException(e.getMessage()); 93 | } 94 | return deviceTokenAsBytes; 95 | } 96 | 97 | /** 98 | *

checkDeviceToken.

99 | * 100 | * @param deviceToken a {@link Object} object. 101 | */ 102 | public static final void checkDeviceToken(Object deviceToken) { 103 | if (deviceToken instanceof String && ((String) deviceToken).length() != 64) { 104 | throw new InvalidDeviceTokenException("device token string must [64] length not [" + ((String) deviceToken).length() + "]"); 105 | } 106 | if (deviceToken instanceof byte[] && ((byte[]) deviceToken).length != 32) { 107 | throw new InvalidDeviceTokenException("device token bytes must [32] length not [" + ((byte[]) deviceToken).length + "]"); 108 | } 109 | } 110 | 111 | public static final void checkNullThrowException(Object target, String name) { 112 | if (null == target) { 113 | throw new NullPointerException(name == null ? "" : name); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/keystore/InvalidKeyStoreException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.keystore; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public class InvalidKeyStoreException extends Exception { 26 | /** 27 | *

Constructor for InvalidKeyStoreException.

28 | * 29 | * @param message a {@link String} object. 30 | */ 31 | public InvalidKeyStoreException(String message) { 32 | super(message); 33 | } 34 | 35 | /** 36 | *

Constructor for InvalidKeyStoreException.

37 | * 38 | * @param message a {@link String} object. 39 | * @param cause a {@link Exception} object. 40 | */ 41 | public InvalidKeyStoreException(String message, Exception cause) { 42 | super(message, cause); 43 | } 44 | 45 | /** 46 | *

Constructor for InvalidKeyStoreException.

47 | * 48 | * @param cause a {@link Exception} object. 49 | */ 50 | public InvalidKeyStoreException(Exception cause) { 51 | super(cause); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/keystore/InvalidKeyStoreFormatException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.keystore; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public class InvalidKeyStoreFormatException extends InvalidKeyStoreException { 26 | /** 27 | *

Constructor for InvalidKeyStoreFormatException.

28 | * 29 | * @param message a {@link String} object. 30 | */ 31 | public InvalidKeyStoreFormatException(String message) { 32 | super(message); 33 | } 34 | 35 | /** 36 | *

Constructor for InvalidKeyStoreFormatException.

37 | * 38 | * @param message a {@link String} object. 39 | * @param cause a {@link Exception} object. 40 | */ 41 | public InvalidKeyStoreFormatException(String message, Exception cause) { 42 | super(message, cause); 43 | } 44 | 45 | /** 46 | *

Constructor for InvalidKeyStoreFormatException.

47 | * 48 | * @param cause a {@link Exception} object. 49 | */ 50 | public InvalidKeyStoreFormatException(Exception cause) { 51 | super(cause); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/keystore/InvalidKeyStorePasswordException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.keystore; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public class InvalidKeyStorePasswordException extends InvalidKeyStoreException { 26 | /** 27 | *

Constructor for InvalidKeyStorePasswordException.

28 | * 29 | * @param msg a {@link String} object. 30 | * @param e a {@link Exception} object. 31 | */ 32 | public InvalidKeyStorePasswordException(String msg, Exception e) { 33 | super(msg, e); 34 | } 35 | 36 | /** 37 | *

Constructor for InvalidKeyStorePasswordException.

38 | * 39 | * @param e a {@link Exception} object. 40 | */ 41 | public InvalidKeyStorePasswordException(Exception e) { 42 | super(e); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/keystore/InvalidKeyStoreTypeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.keystore; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public class InvalidKeyStoreTypeException extends InvalidKeyStoreException { 26 | /** 27 | *

Constructor for InvalidKeyStoreTypeException.

28 | * 29 | * @param msg a {@link String} object. 30 | * @param e a {@link Exception} object. 31 | */ 32 | public InvalidKeyStoreTypeException(String msg, Exception e) { 33 | super(msg, e); 34 | } 35 | 36 | /** 37 | *

Constructor for InvalidKeyStoreTypeException.

38 | * 39 | * @param e a {@link Exception} object. 40 | */ 41 | public InvalidKeyStoreTypeException(Exception e) { 42 | super(e); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/keystore/KeyStoreGetter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.keystore; 20 | 21 | import cn.teaey.apns4j.ApnsException; 22 | import cn.teaey.apns4j.ApnsHelper; 23 | 24 | import javax.crypto.BadPaddingException; 25 | import java.io.*; 26 | import java.security.KeyStore; 27 | import java.security.KeyStoreException; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.NoSuchProviderException; 30 | import java.security.cert.CertificateException; 31 | 32 | /** 33 | * @author teaey(xiaofei.wxf) 34 | * @since 1.0.3 35 | */ 36 | public class KeyStoreGetter { 37 | private final InputStream keyStoreInputStream; 38 | private final String keyStorePwd; 39 | private final KeyStoreType keyStoreType; 40 | 41 | public KeyStoreGetter(InputStream keyStoreInputStream, String keyStorePwd, KeyStoreType keyStoreType) { 42 | ApnsHelper.checkNullThrowException(keyStoreInputStream, "keyStoreInputStream"); 43 | ApnsHelper.checkNullThrowException(keyStoreType, "keyStoreType"); 44 | this.keyStoreInputStream = keyStoreInputStream; 45 | this.keyStorePwd = keyStorePwd; 46 | this.keyStoreType = keyStoreType; 47 | } 48 | 49 | public KeyStoreGetter(File keyStoreFile, String keyStorePwd, KeyStoreType keyStoreType) { 50 | ApnsHelper.checkNullThrowException(keyStoreFile, "keyStoreFile"); 51 | ApnsHelper.checkNullThrowException(keyStoreType, "keyStoreType"); 52 | try { 53 | this.keyStoreInputStream = new FileInputStream(keyStoreFile); 54 | } catch (FileNotFoundException e) { 55 | throw new ApnsException(e); 56 | } 57 | this.keyStorePwd = keyStorePwd; 58 | this.keyStoreType = keyStoreType; 59 | } 60 | 61 | public KeyStoreGetter(String keyStoreFilePath, String keyStorePwd, KeyStoreType keyStoreType) { 62 | ApnsHelper.checkNullThrowException(keyStoreFilePath, "keyStoreFilePath"); 63 | ApnsHelper.checkNullThrowException(keyStoreType, "keyStoreType"); 64 | try { 65 | this.keyStoreInputStream = new FileInputStream(keyStoreFilePath); 66 | } catch (FileNotFoundException e) { 67 | throw new ApnsException(e); 68 | } 69 | this.keyStorePwd = keyStorePwd; 70 | this.keyStoreType = keyStoreType; 71 | } 72 | 73 | public KeyStoreGetter(InputStream keyStoreInputStream, String keyStorePwd) { 74 | ApnsHelper.checkNullThrowException(keyStoreInputStream, "keyStoreInputStream"); 75 | this.keyStoreInputStream = keyStoreInputStream; 76 | this.keyStorePwd = keyStorePwd; 77 | this.keyStoreType = KeyStoreType.PKCS12; 78 | } 79 | 80 | public KeyStoreGetter(File keyStoreFile, String keyStorePwd) { 81 | ApnsHelper.checkNullThrowException(keyStoreFile, "keyStoreFile"); 82 | try { 83 | this.keyStoreInputStream = new FileInputStream(keyStoreFile); 84 | } catch (FileNotFoundException e) { 85 | throw new ApnsException(e); 86 | } 87 | this.keyStorePwd = keyStorePwd; 88 | this.keyStoreType = KeyStoreType.PKCS12; 89 | } 90 | 91 | public KeyStoreGetter(String keyStoreFilePath, String keyStorePwd) { 92 | ApnsHelper.checkNullThrowException(keyStoreFilePath, "keyStoreFilePath"); 93 | try { 94 | this.keyStoreInputStream = new FileInputStream(keyStoreFilePath); 95 | } catch (FileNotFoundException e) { 96 | throw new ApnsException(e); 97 | } 98 | this.keyStorePwd = keyStorePwd; 99 | this.keyStoreType = KeyStoreType.PKCS12; 100 | } 101 | 102 | public KeyStore keyStore() { 103 | try { 104 | KeyStore keyStore = KeyStore.getInstance(this.keyStoreType.name()); 105 | keyStore.load(this.keyStoreInputStream, null == keyStorePwd ? new char[0] : keyStorePwd.toCharArray()); 106 | return keyStore; 107 | } catch (KeyStoreException e) { 108 | if (e.getCause() instanceof NoSuchAlgorithmException || e.getCause() instanceof NoSuchProviderException) { 109 | throw new ApnsException(new InvalidKeyStoreTypeException("(" + this.keyStoreType + ")", e)); 110 | } else { 111 | throw new ApnsException(new InvalidKeyStoreTypeException(e)); 112 | } 113 | } catch (CertificateException e) { 114 | throw new ApnsException(new InvalidKeyStoreFormatException("cant load keystore", e)); 115 | } catch (NoSuchAlgorithmException e) { 116 | throw new ApnsException(new InvalidKeyStoreFormatException("cant load keystore", e)); 117 | } catch (IOException e) { 118 | if (e.getCause() instanceof BadPaddingException) { 119 | throw new ApnsException(new InvalidKeyStorePasswordException("(" + this.keyStorePwd + ")", e)); 120 | } else { 121 | throw new ApnsException(new InvalidKeyStoreFormatException(e)); 122 | } 123 | } 124 | } 125 | 126 | 127 | public String keyStorePwd() { 128 | return this.keyStorePwd; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/keystore/KeyStoreType.java: -------------------------------------------------------------------------------- 1 | package cn.teaey.apns4j.keystore; 2 | 3 | /** 4 | * @author teaey(xiaofei.wxf) 5 | * @since 1.0.3 6 | */ 7 | public enum KeyStoreType { 8 | PKCS12, 9 | JKS 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/ApnsChannel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network; 20 | 21 | import cn.teaey.apns4j.ApnsException; 22 | import cn.teaey.apns4j.ApnsHelper; 23 | import cn.teaey.apns4j.network.async.ApnsFuture; 24 | import cn.teaey.apns4j.network.async.PayloadSender; 25 | import cn.teaey.apns4j.protocol.ApnsPayload; 26 | import cn.teaey.apns4j.protocol.ErrorResp; 27 | 28 | import javax.net.ssl.SSLSocket; 29 | import java.io.IOException; 30 | import java.io.InputStream; 31 | import java.io.OutputStream; 32 | import java.util.LinkedHashMap; 33 | import java.util.Map; 34 | import java.util.concurrent.atomic.AtomicBoolean; 35 | import java.util.concurrent.atomic.AtomicInteger; 36 | 37 | /** 38 | * @author teaey(xiaofei.wxf) 39 | * @since 1.0.3 40 | */ 41 | public class ApnsChannel implements Channel, PayloadSender { 42 | //private static final Logger log = LoggerFactory.getLogger(ApnsChannel.class); 43 | public static final int DEFAULT_TRY_TIMES = 3; 44 | private static final int CACHE_SIZE = 2000; 45 | private static final AtomicInteger COUNTER = new AtomicInteger(0); 46 | private final int id; 47 | private final SecuritySocketFactory socketFactory; 48 | private final AtomicBoolean closed = new AtomicBoolean(false); 49 | private volatile SSLSocket socket; 50 | private volatile InputStream in; 51 | private volatile OutputStream out; 52 | private final int tryTimes; 53 | private volatile LRUCache payloadCache = new LRUCache<>(CACHE_SIZE); 54 | 55 | private class LRUCache extends LinkedHashMap { 56 | private final int capacity; 57 | 58 | public LRUCache(int cacheSize) { 59 | super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); 60 | capacity = cacheSize; 61 | } 62 | 63 | @Override 64 | protected boolean removeEldestEntry(Map.Entry eldest) { 65 | return size() > capacity; 66 | } 67 | } 68 | 69 | private class CacheWapper { 70 | private final int id; 71 | private final byte[] deviceToken; 72 | private final ApnsPayload apnsPayload; 73 | 74 | private CacheWapper(int id, byte[] deviceToken, ApnsPayload apnsPayload) { 75 | this.id = id; 76 | this.deviceToken = deviceToken; 77 | this.apnsPayload = apnsPayload; 78 | } 79 | 80 | public int getId() { 81 | return id; 82 | } 83 | 84 | public byte[] getDeviceToken() { 85 | return deviceToken; 86 | } 87 | 88 | public ApnsPayload getApnsPayload() { 89 | return apnsPayload; 90 | } 91 | } 92 | 93 | public ApnsChannel(SecuritySocketFactory socketFactory) { 94 | this(socketFactory, DEFAULT_TRY_TIMES); 95 | } 96 | 97 | public ApnsChannel(SecuritySocketFactory socketFactory, int tryTimes) { 98 | _detectSocket(); 99 | this.socketFactory = socketFactory; 100 | this.tryTimes = tryTimes; 101 | this.id = COUNTER.incrementAndGet(); 102 | } 103 | 104 | 105 | /** 106 | *

flush.

107 | * 108 | * @throws java.io.IOException if any. 109 | */ 110 | private void flush() throws IOException { 111 | checkClosed(); 112 | out().flush(); 113 | } 114 | 115 | public ApnsFuture send(byte[] deviceTokenBytes, ApnsPayload apnsPayload, int tryTimes, boolean resent) { 116 | checkClosed(); 117 | if (tryTimes < 1) { 118 | tryTimes = 1; 119 | } 120 | int payloadId = ApnsHelper.IDENTIFIER.incrementAndGet(); 121 | ApnsHelper.checkDeviceToken(deviceTokenBytes); 122 | String jsonString = apnsPayload.toJsonString(); 123 | byte[] binaryData = ApnsHelper.toRequestBytes(deviceTokenBytes, jsonString, payloadId, apnsPayload.getExpiry()); 124 | for (int i = 1; i <= tryTimes; i++) { 125 | try { 126 | socket(); 127 | out().write(binaryData); 128 | flush(); 129 | //log.debug("Success send payload : {} to device : {} try : {}", new Object[]{jsonString, deviceTokenBytes, i}); 130 | break; 131 | } catch (IOException e) { 132 | try { 133 | _close(); 134 | } catch (IOException e1) { 135 | } 136 | //log.error("Failed send payload : {} to device : {} try : {}", new Object[]{jsonString, deviceTokenBytes, e}); 137 | if (i == tryTimes) 138 | throw new ApnsException(e); 139 | } 140 | } 141 | if(!resent) { 142 | payloadCache.put(payloadId, new CacheWapper(payloadId, deviceTokenBytes, apnsPayload)); 143 | } 144 | return null; 145 | } 146 | 147 | /** 148 | * Send a payload with a devicetoken bytes(32 bytes) 149 | * 150 | * @param deviceTokenBytes an array of byte. 151 | * @param apnsPayload a {@link ApnsPayload} object. 152 | */ 153 | public ApnsFuture send(byte[] deviceTokenBytes, ApnsPayload apnsPayload) { 154 | checkClosed(); 155 | return this.send(deviceTokenBytes, apnsPayload, tryTimes, false); 156 | } 157 | 158 | /** 159 | * Send a payload with a devicetoken string(length 64) 160 | * 161 | * @param deviceTokenString a {@link String} object. 162 | * @param apnsPayload a {@link ApnsPayload} object. 163 | * @param tryTimes retry times 164 | * @return feture 165 | */ 166 | public ApnsFuture send(String deviceTokenString, ApnsPayload apnsPayload, int tryTimes) { 167 | checkClosed(); 168 | return send(ApnsHelper.toByteArray(deviceTokenString), apnsPayload, tryTimes, false); 169 | } 170 | 171 | /** 172 | * Send a payload with a devicetoken string(length 64) 173 | * 174 | * @param deviceTokenString a {@link String} object. 175 | * @param apnsPayload a {@link ApnsPayload} object. 176 | */ 177 | public ApnsFuture send(String deviceTokenString, ApnsPayload apnsPayload) { 178 | checkClosed(); 179 | return this.send(ApnsHelper.toByteArray(deviceTokenString), apnsPayload); 180 | } 181 | 182 | /** 183 | *

recvErrorResp.

184 | * 185 | * @return a {@link ErrorResp} object. 186 | */ 187 | public ErrorResp recvErrorResp() { 188 | checkClosed(); 189 | try { 190 | if (null == in()) { 191 | return null; 192 | } 193 | byte[] data = new byte[6]; 194 | recv(data); 195 | _close(); 196 | return new ErrorResp(data); 197 | } catch (ApnsException e) { 198 | throw e; 199 | } catch (IOException e) { 200 | throw new ApnsException(e); 201 | } 202 | } 203 | 204 | private void _close() throws IOException { 205 | if (null != socket) { 206 | try { 207 | socket.close(); 208 | } finally { 209 | in = null; 210 | out = null; 211 | socket = null; 212 | } 213 | } 214 | } 215 | 216 | private void _detectSocket() { 217 | Thread t = new Thread(new Runnable() { 218 | @Override 219 | public void run() { 220 | while(!ApnsChannel.this.closed.get()) { 221 | try { 222 | ErrorResp errorResp = recvErrorResp(); 223 | if(null == errorResp) { 224 | Thread.sleep(1000); 225 | continue; 226 | } 227 | LRUCache cache = ApnsChannel.this.payloadCache; 228 | ApnsChannel.this.payloadCache = new LRUCache<>(CACHE_SIZE); 229 | for (CacheWapper cacheWapper : cache.values()) { 230 | if (cacheWapper.getId() > errorResp.getIdentifier()) { 231 | if (ApnsChannel.this.closed.get()) { 232 | break; 233 | } 234 | send(cacheWapper.getDeviceToken(), cacheWapper.getApnsPayload(), 1, true); 235 | } 236 | } 237 | } catch (Exception e) { 238 | 239 | } 240 | } 241 | } 242 | }, "Apns4j-Channel-Detector-" + this.id); 243 | t.start(); 244 | } 245 | 246 | /** 247 | *

close.

248 | */ 249 | @Override 250 | public void close() { 251 | try { 252 | if (this.closed.compareAndSet(false, true)) { 253 | _close(); 254 | } 255 | } catch (IOException e) { 256 | throw new ApnsException(e); 257 | } 258 | } 259 | 260 | private void checkClosed() { 261 | if (closed.get()) { 262 | throw new IllegalStateException("Channel closed, get a new channel from channel factory"); 263 | } 264 | } 265 | 266 | /** 267 | * {@inheritDoc} 268 | */ 269 | @Override 270 | public void send(byte[] data) { 271 | checkClosed(); 272 | try { 273 | out().write(data); 274 | } catch (IOException e) { 275 | try { 276 | _close(); 277 | } catch (IOException e1) { 278 | } 279 | throw new ApnsException(e); 280 | } 281 | } 282 | 283 | /** 284 | * {@inheritDoc} 285 | */ 286 | @Override 287 | public int recv(byte[] data) { 288 | checkClosed(); 289 | try { 290 | return in().read(data); 291 | } catch (IOException e) { 292 | try { 293 | _close(); 294 | } catch (IOException e1) { 295 | } 296 | throw new ApnsException(e); 297 | } 298 | } 299 | 300 | /** 301 | *

socket.

302 | * @throws IOException 303 | */ 304 | protected void socket() throws IOException { 305 | checkClosed(); 306 | if (null != socket && null != in && null != out && !socket.isClosed()) { 307 | return; 308 | } else { 309 | _close(); 310 | this.socket = socketFactory.createSocket(); 311 | this.in = socket.getInputStream(); 312 | this.out = socket.getOutputStream(); 313 | } 314 | } 315 | 316 | /** 317 | *

out.

318 | * 319 | * @return a {@link java.io.OutputStream} object. 320 | */ 321 | protected OutputStream out() { 322 | return this.out; 323 | } 324 | 325 | /** 326 | *

in.

327 | * 328 | * @return a {@link java.io.InputStream} object. 329 | */ 330 | protected InputStream in() { 331 | return this.in; 332 | } 333 | 334 | /** 335 | *

Getter for the field id.

336 | * 337 | * @return a int. 338 | */ 339 | public int getId() { 340 | return id; 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/ApnsChannelFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network; 20 | 21 | /** 22 | * @author teaey(xiaofei.wxf) 23 | * @since 1.0.3 24 | */ 25 | public final class ApnsChannelFactory { 26 | private final SecuritySocketFactory securitySocketFactory; 27 | 28 | public ApnsChannelFactory(SecuritySocketFactory securitySocketFactory) { 29 | this.securitySocketFactory = securitySocketFactory; 30 | } 31 | 32 | public SecuritySocketFactory getSecuritySocketFactory() { 33 | return securitySocketFactory; 34 | } 35 | 36 | public ApnsChannel newChannel() { 37 | return new ApnsChannel(this.securitySocketFactory); 38 | } 39 | 40 | public ApnsChannel newChannel(int tryTimes) { 41 | return new ApnsChannel(this.securitySocketFactory, tryTimes); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/ApnsGateway.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network; 20 | 21 | /** 22 | * @author teaey(xiaofei.wxf) 23 | * @since 1.0.3 24 | */ 25 | public enum ApnsGateway { 26 | DEVELOPMENT("gateway.sandbox.push.apple.com", 2195), 27 | PRODUCTION("gateway.push.apple.com", 2195), 28 | RESTFUL_DEVELOPMENT("api.development.push.apple.com", 443), 29 | RESTFUL_PRODUCTION("api.push.apple.com", 443), 30 | RESTFUL_DEVELOPMENT_BAK("api.development.push.apple.com", 2197), 31 | RESTFUL_PRODUCTION_BAK("api.push.apple.com", 2197); 32 | private final String host; 33 | private final int port; 34 | 35 | ApnsGateway(String host, int port) { 36 | this.host = host; 37 | this.port = port; 38 | } 39 | 40 | public String host() { 41 | return this.host; 42 | } 43 | 44 | public int port() { 45 | return this.port; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/Channel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network; 20 | 21 | import java.io.Closeable; 22 | 23 | /** 24 | * @author teaey 25 | * @since 1.0.0 26 | */ 27 | public interface Channel extends Closeable { 28 | /** 29 | * Writes data.length bytes from the specified byte array 30 | * to this connection 31 | * 32 | * @param data an array of byte. 33 | */ 34 | void send(byte[] data); 35 | 36 | /** 37 | * Reads some number of bytes from the connection and stores them into 38 | * the buffer array data. The number of bytes actually read is 39 | * returned as an integer. 40 | * 41 | * @param data an array of byte. 42 | * @return a int. 43 | */ 44 | int recv(byte[] data); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/SecuritySocketFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network; 20 | 21 | import cn.teaey.apns4j.ApnsException; 22 | import cn.teaey.apns4j.keystore.InvalidKeyStoreException; 23 | 24 | import javax.net.ssl.*; 25 | import java.io.IOException; 26 | import java.security.KeyStore; 27 | import java.security.Security; 28 | import java.security.cert.CertificateException; 29 | import java.security.cert.X509Certificate; 30 | 31 | //import org.bouncycastle.jce.provider.BouncyCastleProvider; 32 | 33 | /** 34 | * @author teaey 35 | * @since 1.0.0 36 | */ 37 | public final class SecuritySocketFactory { 38 | /* The algorithm used by KeyManagerFactory */ 39 | private static final String ALGORITHM = ((Security.getProperty("ssl.KeyManagerFactory.algorithm") == null) ? "sunx509" : Security.getProperty("ssl.KeyManagerFactory.algorithm")); 40 | /* The protocol used to create the SSLSocket */ 41 | private static final String PROTOCOL = "TLS"; 42 | // static 43 | // { 44 | // Security.addProvider(new BouncyCastleProvider()); 45 | // } 46 | private final String host; 47 | private final int port; 48 | private final KeyStore keyStore; 49 | private final String keyStorePwd; 50 | private final SSLSocketFactory sslSocketFactory; 51 | private final TrustManager[] trustManagers = new TrustManager[]{new ServerTrustingTrustManager()}; 52 | 53 | public SecuritySocketFactory(String host, int port, KeyStore keyStore, String keyStorePwd) { 54 | if (host == null) { 55 | throw new NullPointerException("host"); 56 | } 57 | if (keyStore == null) { 58 | throw new NullPointerException("keystore"); 59 | } 60 | if (keyStorePwd == null) { 61 | throw new NullPointerException("keyStorePwd"); 62 | } 63 | this.host = host; 64 | this.port = port; 65 | this.keyStore = keyStore; 66 | this.keyStorePwd = keyStorePwd; 67 | this.sslSocketFactory = createSSLSocketFactoryWithTrustManagers(trustManagers); 68 | } 69 | 70 | public String getHost() { 71 | return host; 72 | } 73 | 74 | public int getPort() { 75 | return port; 76 | } 77 | 78 | public KeyStore getKeyStore() { 79 | return keyStore; 80 | } 81 | 82 | public String getKeyStorePwd() { 83 | return keyStorePwd; 84 | } 85 | 86 | public SSLSocketFactory getSslSocketFactory() { 87 | return sslSocketFactory; 88 | } 89 | 90 | public TrustManager[] getTrustManagers() { 91 | return trustManagers; 92 | } 93 | 94 | /** 95 | *

createSSLSocketFactoryWithTrustManagers.

96 | * 97 | * @param trustManagers an array of {@link javax.net.ssl.TrustManager} objects. 98 | * @return a {@link javax.net.ssl.SSLSocketFactory} object. 99 | */ 100 | protected SSLSocketFactory createSSLSocketFactoryWithTrustManagers(TrustManager[] trustManagers) { 101 | // Get a KeyManager and initialize it 102 | try { 103 | KeyManagerFactory kmf = KeyManagerFactory.getInstance(ALGORITHM); 104 | kmf.init(keyStore, keyStorePwd.toCharArray()); 105 | // Get the SSLContext to help create SSLSocketFactory 106 | SSLContext sslc = SSLContext.getInstance(PROTOCOL); 107 | sslc.init(kmf.getKeyManagers(), trustManagers, null); 108 | return sslc.getSocketFactory(); 109 | } catch (Exception e) { 110 | throw new ApnsException(new InvalidKeyStoreException(e)); 111 | } 112 | } 113 | 114 | /** 115 | * Create a SSLSocket which will be used to send data to Apple 116 | * 117 | * @return the SSLSocket 118 | */ 119 | public SSLSocket createSocket() throws IOException { 120 | return (SSLSocket) sslSocketFactory.createSocket(host, port); 121 | } 122 | 123 | private class ServerTrustingTrustManager implements X509TrustManager { 124 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 125 | throw new CertificateException("not trusted"); 126 | } 127 | 128 | public void checkServerTrusted(X509Certificate[] chain, String authType) { 129 | // trust all servers 130 | } 131 | 132 | public X509Certificate[] getAcceptedIssuers() { 133 | return null; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/async/ApnsFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network.async; 20 | 21 | import cn.teaey.apns4j.protocol.ApnsPayload; 22 | 23 | import java.util.concurrent.ExecutionException; 24 | import java.util.concurrent.Future; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.concurrent.TimeoutException; 27 | 28 | /** 29 | * @author xiaofei.wxf 30 | */ 31 | public class ApnsFuture implements Future { 32 | private final Future future; 33 | private final byte[] deviceToken; 34 | private final ApnsPayload payload; 35 | 36 | public ApnsFuture(Future future, byte[] deviceToken, ApnsPayload payload) { 37 | this.future = future; 38 | this.deviceToken = deviceToken; 39 | this.payload = payload; 40 | } 41 | 42 | @Override 43 | public boolean cancel(boolean mayInterruptIfRunning) { 44 | return future.cancel(mayInterruptIfRunning); 45 | } 46 | 47 | @Override 48 | public boolean isCancelled() { 49 | return future.isCancelled(); 50 | } 51 | 52 | @Override 53 | public boolean isDone() { 54 | return future.isDone(); 55 | } 56 | 57 | @Override 58 | public Object get() throws InterruptedException, ExecutionException { 59 | return future.get(); 60 | } 61 | 62 | @Override 63 | public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 64 | return future.get(timeout, unit); 65 | } 66 | 67 | public byte[] getDeviceToken() { 68 | return deviceToken; 69 | } 70 | 71 | public ApnsPayload getPayload() { 72 | return payload; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/async/ApnsService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network.async; 20 | 21 | import cn.teaey.apns4j.ApnsException; 22 | import cn.teaey.apns4j.ApnsHelper; 23 | import cn.teaey.apns4j.network.ApnsChannelFactory; 24 | import cn.teaey.apns4j.protocol.ApnsPayload; 25 | 26 | import java.util.concurrent.*; 27 | import java.util.concurrent.atomic.AtomicBoolean; 28 | 29 | /** 30 | * @author teaey 31 | * @since 1.0.0 32 | */ 33 | public class ApnsService implements PayloadSender { 34 | private static final int processors = Runtime.getRuntime().availableProcessors(); 35 | private final int size; 36 | private final ApnsChannelFactory apnsChannelFactory; 37 | private final ExecutorService executorService; 38 | private final ExecutorService errorExecutor; 39 | private final AtomicBoolean START = new AtomicBoolean(true); 40 | private final int tryTimes; 41 | private final ThreadLocal> threadSelfConnection = new ThreadLocal() { 42 | protected PayloadSender initialValue() { 43 | return apnsChannelFactory.newChannel(); 44 | } 45 | }; 46 | 47 | public ApnsService(int executorSize, ApnsChannelFactory apnsChannelFactory, int tryTimes) { 48 | this.size = (executorSize > processors) ? processors : executorSize; 49 | this.apnsChannelFactory = apnsChannelFactory; 50 | this.executorService = Executors.newFixedThreadPool(this.size); 51 | this.errorExecutor = Executors.newFixedThreadPool(1); 52 | this.tryTimes = tryTimes; 53 | } 54 | 55 | public ApnsFuture send(String deviceToken, ApnsPayload payload) { 56 | return this.send(ApnsHelper.toByteArray(deviceToken), payload); 57 | } 58 | 59 | public ApnsFuture send(byte[] deviceToken, ApnsPayload payload) { 60 | if (!START.get()) { 61 | throw new AsyncServiceShutdownException("AsynService has shutdown"); 62 | } 63 | Future f = executorService.submit(new APNSAsynTask(deviceToken, payload)); 64 | return new ApnsFuture(f, deviceToken, payload); 65 | } 66 | 67 | /** 68 | *

shutdown.

69 | */ 70 | public void shutdown() { 71 | if (START.compareAndSet(true, false)) { 72 | executorService.shutdown(); 73 | } 74 | } 75 | 76 | public void shutdownNow() { 77 | if (START.compareAndSet(true, false)) { 78 | executorService.shutdownNow(); 79 | } 80 | } 81 | 82 | public void shutdown(long timeout, TimeUnit timeUnit) { 83 | if (START.compareAndSet(true, false)) { 84 | try { 85 | executorService.shutdown(); 86 | executorService.awaitTermination(timeout, timeUnit); 87 | } catch (InterruptedException e) { 88 | throw new ApnsException(e); 89 | } 90 | } 91 | } 92 | 93 | //private static final Logger log = LoggerFactory.getLogger(APNSAsynService.class); 94 | class APNSAsynTask implements Callable { 95 | private final byte[] deviceToken; 96 | private final ApnsPayload payload; 97 | 98 | public APNSAsynTask(byte[] deviceToken, ApnsPayload payload) { 99 | this.deviceToken = deviceToken; 100 | this.payload = payload; 101 | } 102 | 103 | @Override 104 | public Object call() { 105 | try { 106 | return threadSelfConnection.get().send(deviceToken, payload); 107 | } catch (Exception e) { 108 | //log.error("Failed cause IOException deviceToken={} payload={}", deviceToken, payload.toJsonString(), e); 109 | return e; 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/async/AsyncServiceShutdownException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network.async; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public class AsyncServiceShutdownException extends RuntimeException { 26 | /** 27 | *

Constructor for AsyncServiceShutdownException.

28 | * 29 | * @param msg a {@link String} object. 30 | */ 31 | public AsyncServiceShutdownException(String msg) { 32 | super(msg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/network/async/PayloadSender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.network.async; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public interface PayloadSender { 26 | /** 27 | *

send.

28 | * 29 | * @param deviceTokenBytes an array of byte. 30 | * @param payload a T object. 31 | * @return feture 32 | */ 33 | ApnsFuture send(byte[] deviceTokenBytes, T payload); 34 | 35 | /** 36 | *

send.

37 | * 38 | * @param deviceTokenString a {@link String} object. 39 | * @param payload a T object. 40 | * @return feture 41 | */ 42 | ApnsFuture send(String deviceTokenString, T payload); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/ApnsPayload.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * @author teaey 27 | * @since 1.0.0 28 | */ 29 | public class ApnsPayload extends Payload { 30 | private static final String ATTR_APS = "aps"; 31 | private static final String ATTR_ALERT = "alert"; 32 | private static final String ATTR_BADGE = "badge"; 33 | private static final String ATTR_SOUND = "sound"; 34 | private static final String ATTR_ALERT_BODY = "body"; 35 | private static final String ATTR_ALERT_TITLE = "title"; 36 | private static final String ATTR_ALERT_ACTION_LOC_KEY = "action-loc-key"; 37 | private static final String ATTR_ALERT_TITLE_LOC_KEY = "title-loc-key"; 38 | private static final String ATTR_ALERT_TITLE_LOC_ARGS = "title-loc-args"; 39 | private static final String ATTR_ALERT_LOC_KEY = "loc-key"; 40 | private static final String ATTR_ALERT_LOC_ARGS = "loc-args"; 41 | private static final String ATTR_ALERT_LAUNCH_IMAGE = "launch-image"; 42 | private static final String ATTR_CONTENT_AVAILABLE = "content-available"; 43 | 44 | private final Map apsDict = new HashMap(); 45 | 46 | public ApnsPayload() { 47 | addDictionary(ATTR_APS, apsDict); 48 | } 49 | 50 | /** 51 | *

alert.

52 | * 53 | * @param msg a {@link String} object. 54 | * @return ApnsPayload 55 | */ 56 | public ApnsPayload alert(String msg) { 57 | apsDict.put(ATTR_ALERT, msg); 58 | return this; 59 | } 60 | 61 | /** 62 | *

badge.

63 | * 64 | * @param num a int. 65 | * 66 | * @return ApnsPayload 67 | */ 68 | public ApnsPayload badge(int num) { 69 | apsDict.put(ATTR_BADGE, num); 70 | return this; 71 | } 72 | 73 | /** 74 | *

sound.

75 | * 76 | * @param sound a {@link String} object. 77 | * @return ApnsPayload 78 | */ 79 | public ApnsPayload sound(String sound) { 80 | apsDict.put(ATTR_SOUND, sound); 81 | return this; 82 | } 83 | 84 | /** 85 | *

alertBody.

86 | * 87 | * @param body a {@link String} object. 88 | * @return ApnsPayload 89 | */ 90 | public ApnsPayload alertBody(String body) { 91 | ensureAlertMap().put(ATTR_ALERT_BODY, body); 92 | return this; 93 | } 94 | 95 | /** 96 | * added in iOS 8.2 97 | * 98 | * @param title 99 | * @return ApnsPayload 100 | */ 101 | public ApnsPayload alertTitle(String title) { 102 | ensureAlertMap().put(ATTR_ALERT_TITLE, title); 103 | return this; 104 | } 105 | 106 | /** 107 | * added in iOS 8.2 108 | * 109 | * @param titleLocKey 110 | * @return ApnsPayload 111 | */ 112 | public ApnsPayload alertTitleLocKey(String titleLocKey) { 113 | ensureAlertMap().put(ATTR_ALERT_TITLE_LOC_KEY, titleLocKey); 114 | return this; 115 | } 116 | 117 | /** 118 | * added in iOS 8.2 119 | * 120 | * @param titleLocArgs 121 | * @return ApnsPayload 122 | */ 123 | public ApnsPayload alertTitleLocArgs(String titleLocArgs) { 124 | ensureAlertMap().put(ATTR_ALERT_TITLE_LOC_ARGS, titleLocArgs); 125 | return this; 126 | } 127 | 128 | /** 129 | *

alertActionLocKey.

130 | * 131 | * @param actionLocKey a {@link String} object. 132 | * @return ApnsPayload 133 | */ 134 | public ApnsPayload alertActionLocKey(String actionLocKey) { 135 | ensureAlertMap().put(ATTR_ALERT_ACTION_LOC_KEY, actionLocKey); 136 | return this; 137 | } 138 | 139 | /** 140 | *

alertLocKey.

141 | * 142 | * @param locKey a {@link String} object. 143 | * @return ApnsPayload 144 | */ 145 | public ApnsPayload alertLocKey(String locKey) { 146 | ensureAlertMap().put(ATTR_ALERT_LOC_KEY, locKey); 147 | return this; 148 | } 149 | 150 | /** 151 | *

alertLocArgs.

152 | * 153 | * @param args a {@link java.util.List} object. 154 | * @return ApnsPayload 155 | */ 156 | public ApnsPayload alertLocArgs(List args) { 157 | ensureAlertMap().put(ATTR_ALERT_LOC_ARGS, args); 158 | return this; 159 | } 160 | 161 | /** 162 | *

launchImage.

163 | * 164 | * @param args a {@link String} object. 165 | * @return ApnsPayload 166 | */ 167 | public ApnsPayload launchImage(String args) { 168 | ensureAlertMap().put(ATTR_ALERT_LAUNCH_IMAGE, args); 169 | return this; 170 | } 171 | 172 | private Map ensureAlertMap() { 173 | Object ret = apsDict.get(ATTR_ALERT); 174 | if (null == ret || ret instanceof String) { 175 | ret = new HashMap(); 176 | apsDict.put(ATTR_ALERT, ret); 177 | } 178 | return (Map) ret; 179 | } 180 | 181 | public ApnsPayload silent() { 182 | addDictionary(ATTR_CONTENT_AVAILABLE, 1); 183 | return this; 184 | } 185 | 186 | public ApnsPayload extend(String k, Object v) { 187 | apsDict.put(k, v); 188 | return this; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/ErrorResp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | import java.nio.ByteBuffer; 22 | 23 | /** 24 | * @author teaey(xiaofei.wxf) 25 | * @since 1.0.3 26 | */ 27 | public class ErrorResp { 28 | 29 | private final byte command; 30 | private final Status status; 31 | private final int identifier; 32 | 33 | /** 34 | *

Constructor for ErrorResp.

35 | * 36 | * @param data an array of byte. 37 | */ 38 | public ErrorResp(byte[] data) { 39 | if (null == data || data.length != 6) { 40 | throw new InvalidErrorResponse(""); 41 | } 42 | ByteBuffer wrapper = ByteBuffer.wrap(data); 43 | this.command = wrapper.get(); 44 | this.status = Status.resolove(wrapper.get()); 45 | this.identifier = wrapper.getInt(); 46 | } 47 | 48 | /** 49 | *

Constructor for ErrorResp.

50 | * 51 | * @param command a byte. 52 | * @param status a byte. 53 | * @param identifier a int. 54 | */ 55 | public ErrorResp(byte command, byte status, int identifier) { 56 | this.command = command; 57 | this.status = Status.resolove(status); 58 | this.identifier = identifier; 59 | } 60 | 61 | /** 62 | *

Getter for the field command.

63 | * 64 | * @return a byte. 65 | */ 66 | public byte getCommand() { 67 | return command; 68 | } 69 | 70 | /** 71 | *

Getter for the field status.

72 | * 73 | * @return 74 | */ 75 | public Status getStatus() { 76 | return status; 77 | } 78 | 79 | /** 80 | *

Getter for the field identifier.

81 | * 82 | * @return a int. 83 | */ 84 | public int getIdentifier() { 85 | return identifier; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return "ErrorResp{" + 91 | "command=" + command + 92 | ", status=" + status + 93 | ", identifier=" + identifier + 94 | '}'; 95 | } 96 | 97 | public enum Status { 98 | OK(0, "No errors encountered"), 99 | ERROR(Integer.MAX_VALUE, "Unknown"), 100 | ERROR1(1, "Processing error"), 101 | ERROR2(2, "Missing device token"), 102 | ERROR3(3, "Missing topic"), 103 | ERROR4(4, "Missing payload"), 104 | ERROR5(5, "Invalid token size"), 105 | ERROR6(6, "Invalid topic size"), 106 | ERROR7(7, "Invalid payload size"), 107 | ERROR8(8, "Invalid token"), 108 | ERROR10(10, "Shutdown"), 109 | ERROR255(255, "None (unknown)"); 110 | int code; 111 | String desc; 112 | 113 | Status(int code, String desc) { 114 | this.code = code; 115 | this.desc = desc; 116 | } 117 | 118 | public static Status resolove(int code) { 119 | for (Status each : Status.values()) { 120 | if (each.code == code) { 121 | return each; 122 | } 123 | } 124 | return Status.ERROR; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/InvalidDeviceTokenException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public class InvalidDeviceTokenException extends RuntimeException { 26 | /** 27 | *

Constructor for InvalidDeviceTokenException.

28 | */ 29 | public InvalidDeviceTokenException() { 30 | super(); 31 | } 32 | 33 | /** 34 | *

Constructor for InvalidDeviceTokenException.

35 | * 36 | * @param msg a {@link String} object. 37 | */ 38 | public InvalidDeviceTokenException(String msg) { 39 | super(msg); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/InvalidErrorResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | /** 22 | * @author teaey 23 | * @since 1.0.0 24 | */ 25 | public class InvalidErrorResponse extends RuntimeException { 26 | /** 27 | *

Constructor for InvalidErrorResponse.

28 | * 29 | * @param msg a {@link String} object. 30 | */ 31 | public InvalidErrorResponse(String msg) { 32 | super(msg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/JsonParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | import java.util.Iterator; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * @author teaey 27 | * @since 1.0.0 28 | */ 29 | public class JsonParser { 30 | private static final char DOUBLE_QUOTE = '"'; 31 | private static final char COLON = ':'; 32 | private static final char BRACE_L = '{'; 33 | private static final char BRACE_R = '}'; 34 | private static final char BRACKETS_L = '['; 35 | private static final char BRACKETS_R = ']'; 36 | private static final char COMMA = ','; 37 | 38 | /** 39 | *

toJsonBytes.

40 | * 41 | * @param root a {@link java.util.Map} object. 42 | * @return an array of byte. 43 | */ 44 | public static byte[] toJsonBytes(Map root) { 45 | return toBytes(toJsonString(root)); 46 | } 47 | 48 | /** 49 | *

toBytes.

50 | * 51 | * @param jsonString a {@link String} object. 52 | * @return an array of byte. 53 | */ 54 | public static byte[] toBytes(String jsonString) { 55 | return jsonString.getBytes(Protocal.DEF_CHARSET); 56 | } 57 | 58 | /** 59 | *

toJsonString.

60 | * 61 | * @param root a {@link java.util.Map} object. 62 | * @return a {@link String} object. 63 | */ 64 | public static String toJsonString(Map root) { 65 | StringBuilder sb = new StringBuilder(32); 66 | append(sb, root); 67 | int deleteIndex = -1; 68 | while ((deleteIndex = sb.indexOf(",}")) != -1) { 69 | sb.deleteCharAt(deleteIndex); 70 | } 71 | String ret = sb.toString(); 72 | return ret; 73 | } 74 | 75 | static void append(StringBuilder sb, Map jsonMeta) { 76 | sb.append(BRACE_L); 77 | Iterator iter = jsonMeta.keySet().iterator(); 78 | while (iter.hasNext()) { 79 | String eachKey = iter.next(); 80 | Object eachValue = jsonMeta.get(eachKey); 81 | sb.append(DOUBLE_QUOTE).append(eachKey).append(DOUBLE_QUOTE).append(COLON); 82 | if (eachValue instanceof Map) { 83 | append(sb, (Map) eachValue); 84 | } else if (eachValue instanceof List) { 85 | List list = (List) eachValue; 86 | int size = list.size(); 87 | for (int i = 0; i < size; i++) { 88 | Object value = list.get(i); 89 | if (i == 0) { 90 | sb.append(BRACKETS_L); 91 | } 92 | sb.append(value.toString()); 93 | if (i == (size - 1)) { 94 | sb.append(BRACKETS_R); 95 | } else { 96 | sb.append(COMMA); 97 | } 98 | } 99 | } else if (eachValue instanceof String) { 100 | sb.append(DOUBLE_QUOTE).append(eachValue).append(DOUBLE_QUOTE); 101 | } else { 102 | sb.append(eachValue); 103 | } 104 | if (iter.hasNext()) 105 | sb.append(COMMA); 106 | } 107 | sb.append(BRACE_R); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/ListMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | import java.util.AbstractMap; 22 | import java.util.Iterator; 23 | import java.util.Map; 24 | import java.util.Set; 25 | 26 | /** 27 | * @author teaey 28 | * @since 1.0.0 29 | */ 30 | public class ListMap extends AbstractMap implements Map { 31 | Entry[] table; 32 | int size; 33 | 34 | /** 35 | *

Constructor for ListMap.

36 | */ 37 | public ListMap() { 38 | table = new Entry[16]; 39 | } 40 | 41 | /** 42 | *

Constructor for ListMap.

43 | * 44 | * @param initialCapacity a int. 45 | */ 46 | public ListMap(int initialCapacity) { 47 | table = new Entry[initialCapacity]; 48 | } 49 | 50 | private Entry remove(int index) { 51 | rangeCheck(index); 52 | Entry oldValue = table[index]; 53 | int numMoved = size - index - 1; 54 | if (numMoved > 0) 55 | System.arraycopy(table, index + 1, table, index, numMoved); 56 | table[--size] = null; 57 | return oldValue; 58 | } 59 | 60 | private void rangeCheck(int index) { 61 | if (index >= size) { 62 | throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 63 | } 64 | } 65 | 66 | private String outOfBoundsMsg(int index) { 67 | return "Index: " + index + ", Size: " + size; 68 | } 69 | 70 | private Entry index(int index) { 71 | rangeCheck(index); 72 | return table[index]; 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | */ 78 | @Override 79 | public Set> entrySet() { 80 | return null; 81 | } 82 | 83 | static class Entry implements Map.Entry { 84 | final K key; 85 | V value; 86 | 87 | Entry(K key, V value) { 88 | this.key = key; 89 | this.value = value; 90 | } 91 | 92 | @Override 93 | public K getKey() { 94 | return key; 95 | } 96 | 97 | @Override 98 | public V getValue() { 99 | return value; 100 | } 101 | 102 | @Override 103 | public V setValue(V value) { 104 | V oldValue = value; 105 | this.value = value; 106 | return oldValue; 107 | } 108 | 109 | public String toString() { 110 | return "[" + getKey() + ":" + getValue() + "]"; 111 | } 112 | } 113 | 114 | private abstract class ListIterator implements Iterator> { 115 | int index; 116 | 117 | ListIterator() { 118 | } 119 | 120 | @Override 121 | public boolean hasNext() { 122 | return index != (size() - 1); 123 | } 124 | 125 | @Override 126 | public Map.Entry next() { 127 | return index(++index); 128 | } 129 | 130 | @Override 131 | public void remove() { 132 | ListMap.this.remove(index); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/Payload.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | /** 27 | * @author teaey 28 | * @since 1.0.0 29 | */ 30 | public abstract class Payload { 31 | 32 | private static final int DEF_EXPIRY = (int) TimeUnit.DAYS.toSeconds(1); 33 | private final Map root = new HashMap(); 34 | /** 35 | * 36 | */ 37 | private int expiry = ((int) System.currentTimeMillis() / 1000) + DEF_EXPIRY; 38 | 39 | /** 40 | *

addDictionary.

41 | * 42 | * @param key a {@link String} object. 43 | * @param value a {@link Object} object. 44 | * @return a {@link Object} object. 45 | */ 46 | protected Object addDictionary(String key, Object value) { 47 | return root.put(key, value); 48 | } 49 | 50 | /** 51 | *

removeDictionary.

52 | * 53 | * @param key a {@link String} object. 54 | * @return a {@link Object} object. 55 | */ 56 | public Object removeDictionary(String key) { 57 | return root.remove(key); 58 | } 59 | 60 | /** 61 | *

toJsonString.

62 | * 63 | * @return a {@link String} object. 64 | */ 65 | public String toJsonString() { 66 | return JsonParser.toJsonString(root); 67 | } 68 | 69 | /** 70 | *

toJsonBytes.

71 | * 72 | * @return an array of byte. 73 | */ 74 | public byte[] toJsonBytes() { 75 | return toJsonString().getBytes(Protocal.DEF_CHARSET); 76 | } 77 | 78 | /** 79 | *

Getter for the field expiry.

80 | * 81 | * @return a int. 82 | */ 83 | public int getExpiry() { 84 | return expiry; 85 | } 86 | 87 | /** 88 | *

Setter for the field expiry.

89 | * 90 | * @param expiry a int. 91 | */ 92 | public void setExpiry(int expiry) { 93 | this.expiry = expiry; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/cn/teaey/apns4j/protocol/Protocal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2015 The Apns4j Project 4 | * * 5 | * * The Netty Project licenses this file to you under the Apache License, 6 | * * version 2.0 (the "License"); you may not use this file except in compliance 7 | * * with the License. You may obtain a copy of the License at: 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * * License for the specific language governing permissions and limitations 15 | * * under the License. 16 | * 17 | */ 18 | 19 | package cn.teaey.apns4j.protocol; 20 | 21 | import java.nio.charset.Charset; 22 | 23 | /** 24 | * @author teaey 25 | * @since 1.0.0 26 | */ 27 | public class Protocal { 28 | /** 29 | * Constant DEF_CHARSET 30 | */ 31 | public static final Charset DEF_CHARSET = Charset.forName("UTF-8"); 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/cn/teaey/apns4j/ActionButtonTest.java: -------------------------------------------------------------------------------- 1 | package cn.teaey.apns4j; 2 | 3 | import cn.teaey.apns4j.protocol.ApnsPayload; 4 | import org.junit.After; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author teaey(xiaofei.wxf) 9 | * @since 1.0.3 10 | */ 11 | public class ActionButtonTest { 12 | 13 | @Test 14 | public void actionLocKey() { 15 | ApnsPayload apnsPayload = Apns4j.newPayload() 16 | .alertBody("Push by apns4j") 17 | .alertActionLocKey("FixMe"); 18 | System.out.println(apnsPayload.toJsonString()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/cn/teaey/apns4j/AlertTest.java: -------------------------------------------------------------------------------- 1 | package cn.teaey.apns4j; 2 | 3 | import cn.teaey.apns4j.protocol.ApnsPayload; 4 | import org.junit.After; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author teaey(xiaofei.wxf) 9 | * @since 1.0.3 10 | */ 11 | public class AlertTest { 12 | 13 | @Test 14 | public void alert() throws InterruptedException { 15 | //create & init notify payload 16 | ApnsPayload apnsPayload = Apns4j.newPayload() 17 | .alertTitle("Title") 18 | .alertBody("Pushed by apns4j") 19 | .extend("k", "v") 20 | .sound("default"); 21 | System.out.println(apnsPayload.toJsonString()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/cn/teaey/apns4j/BadgeTest.java: -------------------------------------------------------------------------------- 1 | package cn.teaey.apns4j; 2 | 3 | import cn.teaey.apns4j.protocol.ApnsPayload; 4 | import org.junit.After; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author teaey(xiaofei.wxf) 9 | * @since 1.0.3 10 | */ 11 | public class BadgeTest { 12 | 13 | @Test 14 | public void badge() { 15 | ApnsPayload payload = Apns4j.newPayload() 16 | .badge(4); 17 | System.out.println(payload.toJsonString()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/cn/teaey/apns4j/MainTest.java: -------------------------------------------------------------------------------- 1 | package cn.teaey.apns4j; 2 | 3 | import cn.teaey.apns4j.network.ApnsChannel; 4 | import cn.teaey.apns4j.network.ApnsChannelFactory; 5 | import cn.teaey.apns4j.network.async.ApnsFuture; 6 | import cn.teaey.apns4j.network.async.ApnsService; 7 | import cn.teaey.apns4j.protocol.ApnsPayload; 8 | import org.junit.After; 9 | import org.junit.Test; 10 | 11 | import java.util.concurrent.ExecutionException; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * @author teaey(xiaofei.wxf) 16 | * @since 1.0.3 17 | */ 18 | public class MainTest { 19 | static final ApnsChannelFactory apnsChannelFactory = Apns4j.newChannelFactoryBuilder().keyStoreMeta(TestConts.keyStorePath).keyStorePwd(TestConts.keyStorePwd).build(); 20 | static final ApnsChannel apnsChannel = apnsChannelFactory.newChannel(); 21 | static final ApnsService apnsService = new ApnsService(3, apnsChannelFactory, 3); 22 | public static void main(String[] args) { 23 | ApnsPayload apnsPayload = Apns4j.newPayload() 24 | .alertTitle("Title") 25 | .alertBody("Pushed by apns4j") 26 | .extend("k", "v") 27 | .sound("default"); 28 | //send via channel 29 | apnsChannel.send(TestConts.deviceToken, apnsPayload); 30 | //send async via service 31 | ApnsFuture apnsFuture = apnsService.send(TestConts.deviceToken, apnsPayload); 32 | apnsService.shutdown(3, TimeUnit.SECONDS); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/cn/teaey/apns4j/TestConts.java: -------------------------------------------------------------------------------- 1 | package cn.teaey.apns4j; 2 | 3 | /** 4 | * @author teaey(xiaofei.wxf) 5 | * @since 1.0.3 6 | */ 7 | public interface TestConts { 8 | String deviceToken = "32252032dc502ded559698b085227799af59abd4d8343f4027012f156f2edc23"; 9 | String deviceToken2 = "a9d04e8d98adb00fa1a62992901b7990f03ac9ba200635c85cbcfdeb464d85ee"; 10 | 11 | String keyStorePath = "/Users/teaey/Documents/apns4j/aps_development.p12"; 12 | String keyStorePwd = "1234"; 13 | } 14 | --------------------------------------------------------------------------------