├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── avos │ └── avoscloud │ ├── AVOSCloud.java │ ├── AVPush.java │ └── internal │ ├── MasterKeyConfiguration.java │ └── impl │ ├── JavaAppConfiguration.java │ ├── JavaRequestSignImplementation.java │ ├── Log4j2Implementation.java │ └── SimplePersistence.java └── test ├── java └── com │ └── avos │ └── avoscloud │ ├── DisableHookTest.java │ └── TestApp.java └── resources └── log4j2.xml /.gitignore: -------------------------------------------------------------------------------- 1 | java-sdk/target/ 2 | java-sdk/.project 3 | java-sdk/.classpath 4 | java-sdk/.settings 5 | .project 6 | .settings 7 | .classpath 8 | leancloud-utils/target 9 | leancloud-utils/.project 10 | leancloud-utils/.settings 11 | leancloud-utils/.classpath 12 | /target/ 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SDK 维护期说明 2 | 3 | 我们于 2018 年 9 月推出了新的 Java Unified SDK,兼容纯 Java、云引擎和 Android 等多个平台,此 java-sdk 目前进入维护状态,直到 2019 年 9 月底停止维护。 欢迎大家切换到新的 Unified SDK,具体使用方法详见 [Unified SDK Wiki](https://github.com/leancloud/java-unified-sdk/wiki)。 4 | 5 | ## LeanCloud Java SDK 6 | 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 8 | ![Platform](https://img.shields.io/badge/platform-java-3cbe00.svg) 9 | 10 | java-sdk is the souce code for Java SDK 11 | 12 | leancloud-utils is the mock interface for android platform features which doesn't exist on Java platform 13 | 14 | ## Features 15 | * [x] Data Storage 16 | * [x] Object Query 17 | * [x] Cloud Engine 18 | * [x] File Storage 19 | * [x] Short Message Service 20 | 21 | ## Communication 22 | * If you **have some advice**, open an issue. 23 | * If you **found a bug**, open an issue, or open a ticket in [LeanTicket][LeanTicket]. 24 | * If you **want to contribute**, submit a pull request. 25 | 26 | 27 | ## Installation 28 | 29 | ### maven 30 | 31 | add dependency: 32 | 33 | ``` xml 34 | 35 | cn.leancloud 36 | java-sdk 37 | 0.1.6 38 | 39 | ``` 40 | 41 | ### gradle 42 | 43 | add dependency: 44 | 45 | ``` gradle 46 | compile 'cn.leancloud:java-sdk:0.1.+' 47 | 48 | ``` 49 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | cn.leancloud 5 | java-sdk 6 | 0.2.2-SNAPSHOT 7 | jar 8 | 9 | java-sdk 10 | LeanCloud Storage, Engine, SMS SDK 11 | https://github.com/leancloud/java-sdk 12 | 13 | 14 | 15 | The Apache License, Version 2.0 16 | http://www.apache.org/licenses/LICENSE-2.0.txt 17 | 18 | 19 | 20 | 21 | 22 | LeanCloud Engineer 23 | eng@leancloud.rocks 24 | LeanCloud 25 | https://leancloud.cn 26 | 27 | 28 | 29 | 30 | scm:git:https://github.com/leancloud/java-sdk.git 31 | scm:git:git@github.com:leancloud/java-sdk.git 32 | http://github.com/leancloud/java-sdk/tree/master 33 | HEAD 34 | 35 | 36 | 37 | ossrh 38 | https://oss.sonatype.org/content/repositories/snapshots 39 | 40 | 41 | ossrh 42 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 43 | 44 | 45 | 46 | 47 | UTF-8 48 | 1.6 49 | 50 | 51 | 52 | 53 | junit 54 | junit 55 | 3.8.1 56 | test 57 | 58 | 59 | cn.leancloud 60 | fastjson-leancloud 61 | 1.1.39 62 | 63 | 64 | org.apache.logging.log4j 65 | log4j-api 66 | 2.5 67 | 68 | 69 | org.json 70 | json 71 | 20160212 72 | 73 | 74 | org.apache.logging.log4j 75 | log4j-core 76 | 2.5 77 | 78 | 79 | cn.leancloud 80 | okhttp-leancloud 81 | 2.6.0 82 | 83 | 84 | cn.leancloud 85 | leancloud-common 86 | [0.2.0,0.3.0) 87 | 88 | 89 | cn.leancloud 90 | android-stub 91 | 0.1.1 92 | 93 | 94 | 95 | org.apache.httpcomponents 96 | httpcore 97 | 4.4.4 98 | 99 | 100 | 101 | org.apache.httpcomponents 102 | httpclient 103 | 4.5.2 104 | 105 | 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-javadoc-plugin 111 | 2.10.4 112 | 113 | false 114 | 115 | 116 | 117 | attach-javadocs 118 | 119 | jar 120 | 121 | 122 | 123 | 124 | 125 | org.apache.maven.plugins 126 | maven-source-plugin 127 | 2.2.1 128 | 129 | 130 | attach-sources 131 | 132 | jar-no-fork 133 | 134 | 135 | 136 | 137 | 138 | org.apache.maven.plugins 139 | maven-gpg-plugin 140 | 1.5 141 | 142 | 143 | sign-artifacts 144 | verify 145 | 146 | sign 147 | 148 | 149 | 150 | 151 | 152 | org.sonatype.plugins 153 | nexus-staging-maven-plugin 154 | 1.6.7 155 | true 156 | 157 | ossrh 158 | https://oss.sonatype.org/ 159 | true 160 | 161 | 162 | 163 | org.apache.maven.plugins 164 | maven-release-plugin 165 | 2.5.3 166 | 167 | 168 | org.apache.maven.plugins 169 | maven-compiler-plugin 170 | 3.6.1 171 | 172 | ${jdk.version} 173 | ${jdk.version} 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-project-info-reports-plugin 184 | 2.7 185 | 186 | false 187 | 188 | 189 | 190 | org.codehaus.mojo 191 | cobertura-maven-plugin 192 | 2.6 193 | 194 | 195 | html 196 | xml 197 | 198 | 199 | 200 | 201 | org.apache.maven.plugins 202 | maven-surefire-plugin 203 | 2.15 204 | 205 | 206 | ${surefireArgLine} 207 | 208 | ${skip.unit.tests} 209 | 210 | 211 | **/NotTest*.java 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /src/main/java/com/avos/avoscloud/AVOSCloud.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud; 2 | 3 | import java.util.Date; 4 | import java.util.Map; 5 | 6 | import com.alibaba.fastjson.JSON; 7 | import com.avos.avoscloud.callback.AVServerDateCallback; 8 | import com.avos.avoscloud.internal.InternalConfigurationController; 9 | import com.avos.avoscloud.internal.InternalDate; 10 | import com.avos.avoscloud.internal.InternalSMS; 11 | import com.avos.avoscloud.internal.impl.DefaultAppRouter; 12 | import com.avos.avoscloud.internal.impl.JavaAppConfiguration; 13 | import com.avos.avoscloud.internal.impl.JavaRequestSignImplementation; 14 | import com.avos.avoscloud.internal.impl.Log4j2Implementation; 15 | import com.avos.avoscloud.internal.impl.SimplePersistence; 16 | 17 | /** 18 | * The AVOSCloud class contains static functions that handle global configuration for the AVOSCloud 19 | * library. 20 | */ 21 | public class AVOSCloud { 22 | 23 | static final String AV_CLOUD_CACHE_EXPIRE_AUTO_CLEAN_KEY = "AV_CLOUD_CACHE_EXPIRE_AUTO_CLEAN_KEY"; 24 | static final String AV_CLOUD_CACHE_EXPIRE_DATE_KEY = "AV_CLOUD_CACHE_EXPIRE_DATE_KEY"; 25 | static final Integer AV_CLOUD_CACHE_DEFAULT_EXPIRE_DATE = 30; 26 | static final String AV_CLOUD_CACHE_EXPIRE_KEY_ZONE = "AV_CLOUD_CACHE_EXPIRE_KEY_ZONE"; 27 | static final String AV_CLOUD_API_VERSION_KEY_ZONE = "AV_CLOUD_API_VERSION_KEY_ZONE"; 28 | static final String AV_CLOUD_API_VERSION_KEY = "AV_CLOUD_API_VERSION"; 29 | 30 | private static boolean isCN = true; 31 | 32 | /** 33 | * Set network timeout in milliseconds.default is 10 seconds. 34 | * 35 | * @param timeoutInMills 超时时间 36 | */ 37 | public static void setNetworkTimeout(int timeoutInMills) { 38 | InternalConfigurationController.globalInstance().getClientConfiguration() 39 | .setNetworkTimeoutInMills(timeoutInMills); 40 | } 41 | 42 | /** 43 | * Returns the network timeout in milliseconds.It's 15 seconds by default. 44 | * 45 | * @return 超时时间 46 | */ 47 | public static int getNetworkTimeout() { 48 | return InternalConfigurationController.globalInstance().getClientConfiguration() 49 | .getNetworkTimeoutInMills(); 50 | } 51 | 52 | static { 53 | JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 54 | } 55 | 56 | private AVOSCloud() {} 57 | 58 | /** 59 | *

60 | * Authenticates this client as belonging to your application. This must be called before your 61 | * application can use the AVOSCloud library. The recommended way is to put a call to 62 | * AVOSCloud.initialize in each of your onCreate methods. An example: 63 | *

64 | * 65 | * @param applicationId The application id provided in the AVOSCloud dashboard. 66 | * @param clientKey The client key provided in the AVOSCloud dashboard. 67 | * @param masterKey The master key provided in the AVOSCloud dashboard. 68 | */ 69 | 70 | public static void initialize(String applicationId, String clientKey, String masterKey) { 71 | JavaAppConfiguration configuration = JavaAppConfiguration.instance(); 72 | configuration.setIsCN(isCN); 73 | configuration.setApplicationId(applicationId); 74 | configuration.setClientKey(clientKey); 75 | configuration.setMasterKey(masterKey); 76 | 77 | InternalConfigurationController.Builder builder = new InternalConfigurationController.Builder(); 78 | builder.setAppConfiguration(configuration) 79 | .setInternalRequestSign(JavaRequestSignImplementation.instance()) 80 | .setInternalPersistence(SimplePersistence.instance()) 81 | .setInternalLogger(Log4j2Implementation.instance()) 82 | .setAppRouter(DefaultAppRouter.instance()); 83 | 84 | builder.build(); 85 | InternalConfigurationController.globalInstance().getAppRouter().updateServerHosts(); 86 | } 87 | 88 | public static void useAVCloudUS() { 89 | isCN = false; 90 | } 91 | 92 | public static void useAVCloudCN() { 93 | isCN = true; 94 | } 95 | 96 | public static boolean showInternalDebugLog() { 97 | return Log4j2Implementation.instance().showInternalDebugLog(); 98 | } 99 | 100 | public static void setDebugLogEnabled(boolean enable) { 101 | Log4j2Implementation.instance().setDebugEnabled(enable); 102 | } 103 | 104 | public static boolean isDebugLogEnabled() { 105 | return Log4j2Implementation.instance().isDebugEnabled() 106 | || Log4j2Implementation.instance().showInternalDebugLog(); 107 | } 108 | 109 | /** 110 | * 请求发送短信验证码 111 | * 112 | * 113 | * 短信示范为: 您正在{name}中进行{op},您的验证码是:{Code},请输入完整验证,有效期为:{ttl} 114 | * 115 | * 116 | * @param phone 目标手机号码(必选) 117 | * @param name 应用名,值为null 则默认是您的应用名 118 | * @param op  验证码的目标操作,值为null,则默认为“短信验证” 119 | * @param ttl 验证码过期时间,单位分钟。如果是0,则默认为10分钟 120 | * @throws AVException 发送短信异常 121 | */ 122 | public static void requestSMSCode(String phone, String name, String op, int ttl) 123 | throws AVException { 124 | InternalSMS.requestSMSCode(phone, name, op, ttl); 125 | } 126 | 127 | /** 128 | * 通过短信模板来发送短信验证码 129 | * 130 | * 131 | * @param phone 目标手机号码(必选) 132 | * @param templateName 短信模板名称 133 | * @param env 需要注入的变量env 134 | * @throws AVException 发送短信异常 135 | */ 136 | public static void requestSMSCode(String phone, String templateName, Map env) 137 | throws AVException { 138 | InternalSMS.requestSMSCode(phone, templateName, env); 139 | } 140 | 141 | /** 142 | * 请求发送短信验证码 143 | * 144 | * 145 | * 短信示范为: 您正在{应用名称}中进行短信验证,您的验证码是:{Code},请输入完整验证,有效期为:10分钟 146 | * 147 | * 148 | * @param phone 目标手机号码 149 | * @throws AVException 发送短信异常 150 | */ 151 | public static void requestSMSCode(String phone) throws AVException { 152 | InternalSMS.requestSMSCode(phone, null, null, 0); 153 | } 154 | 155 | /** 156 | * 请求发送语音验证码,验证码会以电话形式打给目标手机 157 | * 158 | * @param phoneNumber 目标手机号 159 | * @throws AVException 发送短信异常 160 | */ 161 | public static void requestVoiceCode(String phoneNumber) throws AVException { 162 | InternalSMS.requestVoiceCode(phoneNumber, null); 163 | } 164 | 165 | 166 | /** 167 | * 验证验证码 168 | * 169 | * @param code 验证码 170 | * @param mobilePhoneNumber 手机号码 171 | * @throws AVException 发送短信异常 172 | */ 173 | public static void verifySMSCode(String code, String mobilePhoneNumber) throws AVException { 174 | InternalSMS.verifySMSCode(code, mobilePhoneNumber); 175 | } 176 | 177 | /** 178 | * 验证验证码 179 | * 180 | * @param code 验证码 181 | * @param mobilePhoneNumber 手机号码 182 | * @throws AVException 发送短信异常 183 | */ 184 | public static void verifyCode(String code, String mobilePhoneNumber) throws AVException { 185 | InternalSMS.verifySMSCode(code, mobilePhoneNumber); 186 | } 187 | 188 | /** 189 | * 获取服务器端当前时间 190 | * 191 | * @return 服务器端当前时间 192 | * @throws AVException 发送短信异常 193 | */ 194 | public static Date getServerDate() throws AVException { 195 | return InternalDate.getServerDate(); 196 | } 197 | 198 | /** 199 | * 获取服务器端当前时间 200 | * 201 | * @param callback 获取时间回调 202 | */ 203 | public static void getServerDateInBackground(AVServerDateCallback callback) { 204 | InternalDate.getServerDateInBackground(callback); 205 | } 206 | 207 | public static void setShouldUseMasterKey(boolean should) { 208 | JavaRequestSignImplementation.instance().setUseMasterKey(should); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/com/avos/avoscloud/AVPush.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud; 2 | 3 | import java.util.Collection; 4 | import java.util.Date; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Iterator; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | import org.json.JSONObject; 12 | 13 | import com.alibaba.fastjson.JSON; 14 | import com.avos.avoscloud.internal.InternalConfigurationController; 15 | 16 | /** 17 | *

18 | * The AVPush is a local representation of data that can be sent as a push notification. 19 | *

20 | *

21 | * The typical workflow for sending a push notification from the client is to construct a new 22 | * AVPush, use the setter functions to fill it with data, and then use AVPush.sendInBackground() to 23 | * send it. 24 | *

25 | */ 26 | public class AVPush { 27 | private static final String INSTALLATIONTAG = "_Installation"; 28 | private static final String deviceTypeTag = "deviceType"; 29 | private static final Set DEVICE_TYPES = new HashSet(); 30 | static { 31 | DEVICE_TYPES.add("android"); 32 | DEVICE_TYPES.add("ios"); 33 | } 34 | private final Set channelSet; 35 | private com.avos.avoscloud.AVQuery pushQuery; 36 | private String cql; 37 | private long expirationTime; 38 | private long expirationTimeInterval; 39 | private final Set pushTarget; 40 | private final Map pushData; 41 | private volatile AVObject notification; 42 | private Date pushDate = null; 43 | private boolean production = true; 44 | 45 | static { 46 | AVPowerfulUtils.createSettings(AVPush.class.getSimpleName(), "push", ""); 47 | } 48 | 49 | /** 50 | * Creates a new push notification. The default channel is the empty string, also known as the 51 | * global broadcast channel, but this value can be overridden using AVPush.setChannel(String), 52 | * AVPush.setChannels(Collection) or AVPush.setQuery(AVQuery). Before sending the push 53 | * notification you must call either AVPush.setMessage(String) or AVPush.setData(JSONObject). 54 | */ 55 | public AVPush() { 56 | channelSet = new HashSet(); 57 | pushData = new HashMap(); 58 | pushTarget = new HashSet(DEVICE_TYPES); 59 | pushQuery = new AVQuery(INSTALLATIONTAG); 60 | } 61 | 62 | public Set getChannelSet() { 63 | return channelSet; 64 | } 65 | 66 | 67 | /** 68 | * 返回推送后创建的_Notification对象。 69 | * 70 | * @return _Notification对象 71 | */ 72 | public AVObject getNotification() { 73 | return notification; 74 | } 75 | 76 | public AVQuery getPushQuery() { 77 | return pushQuery; 78 | } 79 | 80 | public Date getPushDate() { 81 | return pushDate; 82 | } 83 | 84 | public long getExpirationTime() { 85 | return expirationTime; 86 | } 87 | 88 | public long getExpirationTimeInterval() { 89 | return expirationTimeInterval; 90 | } 91 | 92 | public Set getPushTarget() { 93 | return pushTarget; 94 | } 95 | 96 | public Map getPushData() { 97 | return pushData; 98 | } 99 | 100 | /** 101 | * Clears both expiration values, indicating that the notification should never expire. 102 | */ 103 | public void clearExpiration() { 104 | expirationTime = 0L; 105 | expirationTimeInterval = 0L; 106 | } 107 | 108 | /** 109 | * Sends this push notification while blocking this thread until the push notification has 110 | * successfully reached the AVOSCloud servers. Typically, you should use AVPush.sendInBackground() 111 | * instead of this, unless you are managing your own threading. 112 | */ 113 | public void send() { 114 | sendInBackground(true, null); 115 | } 116 | 117 | /** 118 | * A helper method to concisely send a push to a query. This method is equivalent to 119 | * 120 | *
121 |    * AVPush push = new AVPush();
122 |    * push.setData(data);
123 |    * push.setQuery(query);
124 |    * push.sendInBackground();
125 |    * 
126 | * 127 | * @param data The entire data of the push message. See the push guide for more details on the 128 | * data format. 129 | * @param query A AVInstallation query which specifies the recipients of a push. 130 | * @throws AVException if query is not valid 131 | */ 132 | static void sendDataInBackground(JSONObject data, AVQuery query) 133 | throws AVException { 134 | if (!query.getClassName().equals(INSTALLATIONTAG)) { 135 | throw new AVException(AVException.OTHER_CAUSE, "only installation query is valid"); 136 | } 137 | AVPush push = new AVPush(); 138 | push.setData(data); 139 | push.setQuery(query); 140 | push.sendInBackground(); 141 | } 142 | 143 | /** 144 | * A helper method to concisely send a push to a query. This method is equivalent to 145 | * 146 | *
147 |    * AVPush push = new AVPush();
148 |    * push.setData(data);
149 |    * push.setQuery(query);
150 |    * push.sendInBackground(callback);
151 |    * 
152 | * 153 | * @param data The entire data of the push message. See the push guide for more details on the 154 | * data format. 155 | * @param query A AVInstallation query which specifies the recipients of a push. 156 | * @param callback callback.done(e) is called when the send completes. 157 | */ 158 | public static void sendDataInBackground(JSONObject data, AVQuery query, 159 | SendCallback callback) { 160 | if (!query.getClassName().equals(INSTALLATIONTAG)) { 161 | if (callback != null) { 162 | callback.done(new AVException(AVException.OTHER_CAUSE, "only installation query is valid")); 163 | } 164 | } 165 | AVPush push = new AVPush(); 166 | push.setData(data); 167 | push.setQuery(query); 168 | push.sendInBackground(false, callback); 169 | } 170 | 171 | /** 172 | * Sends this push notification in a background thread. This is preferable to using send(), unless 173 | * your code is already running from a background thread. 174 | */ 175 | public void sendInBackground() { 176 | sendInBackground(false, null); 177 | } 178 | 179 | /** 180 | * Sends this push notification in a background thread. This is preferable to using send(), unless 181 | * your code is already running from a background thread. 182 | * 183 | * @param callback callback.done(e) is called when the send completes. 184 | */ 185 | public void sendInBackground(SendCallback callback) { 186 | sendInBackground(false, callback); 187 | } 188 | 189 | private Map pushChannelsData() { 190 | return AVUtils.createStringObjectMap("channels", channelSet); 191 | } 192 | 193 | private Map postDataMap() throws AVException { 194 | Map map = new HashMap(); 195 | 196 | if (pushQuery != null) { 197 | if (pushTarget.size() == 0) { 198 | pushQuery.whereNotContainedIn(deviceTypeTag, DEVICE_TYPES); 199 | } else if (pushTarget.size() == 1) { 200 | pushQuery.whereEqualTo(deviceTypeTag, pushTarget.toArray()[0]); 201 | } 202 | Map pushParameters = pushQuery.assembleParameters(); 203 | if (pushParameters.keySet().size() > 0 && !AVUtils.isBlankString(cql)) { 204 | throw new IllegalStateException("You can't use AVQuery and Cloud query at the same time."); 205 | } 206 | for (String k : pushParameters.keySet()) { 207 | map.put(k, JSON.parse(pushParameters.get(k))); 208 | } 209 | } 210 | if (!AVUtils.isBlankString(cql)) { 211 | map.put("cql", cql); 212 | } 213 | if (channelSet.size() > 0) { 214 | map.putAll(pushChannelsData()); 215 | } 216 | 217 | if (this.expirationTime > 0) { 218 | map.put("expiration_time", this.expirationDateTime()); 219 | } 220 | if (this.expirationTimeInterval > 0) { 221 | map.put("push_time", AVUtils.stringFromDate(new Date())); 222 | map.put("expiration_interval", new Long(this.expirationTimeInterval)); 223 | } 224 | if (this.pushDate != null) { 225 | map.put("push_time", AVUtils.stringFromDate(pushDate)); 226 | } 227 | 228 | if (!production) { 229 | map.put("prod", "dev"); 230 | } 231 | 232 | map.putAll(pushData); 233 | return map; 234 | } 235 | 236 | /** 237 | * Sends this push notification in a background thread. This is preferable to using send(), unless 238 | * your code is already running from a background thread. 239 | * 240 | * @param callback callback.done(e) is called when the send completes. 241 | */ 242 | private void sendInBackground(boolean sync, SendCallback callback) { 243 | final SendCallback internalCallback = callback; 244 | String path = "push"; 245 | try { 246 | Map map = postDataMap(); 247 | String jsonString = AVUtils.jsonStringFromMapWithNull(map); 248 | PaasClient.storageInstance().postObject(path, jsonString, sync, new GenericObjectCallback() { 249 | @Override 250 | public void onSuccess(String content, AVException e) { 251 | notification = new AVObject("_Notification"); 252 | AVUtils.copyPropertiesFromJsonStringToAVObject(content, notification); 253 | if (internalCallback != null) { 254 | internalCallback.internalDone(null); 255 | } 256 | } 257 | 258 | @Override 259 | public void onFailure(Throwable error, String content) { 260 | if (internalCallback != null) { 261 | internalCallback.internalDone(AVErrorUtils.createException(error, content)); 262 | } 263 | } 264 | }); 265 | } catch (AVException e) { 266 | if (callback != null) { 267 | callback.internalDone(e); 268 | } else { 269 | LogUtil.log.e("AVPush sent exception", e); 270 | } 271 | } 272 | } 273 | 274 | /** 275 | * A helper method to concisely send a push message to a query. This method is equivalent to 276 | * 277 | *
278 |    * AVPush push = new AVPush();
279 |    * push.setMessage(message);
280 |    * push.setQuery(query);
281 |    * push.sendInBackground();
282 |    * 
283 | * 284 | * @param message The message that will be shown in the notification. 285 | * @param query A AVInstallation query which specifies the recipients of a push. 286 | */ 287 | public static void sendMessageInBackground(String message, AVQuery query) { 288 | if (!query.getClassName().equals(INSTALLATIONTAG)) { 289 | InternalConfigurationController.globalInstance().getInternalLogger() 290 | .e(AVPush.class.getSimpleName(), "only installation query is valid"); 291 | return; 292 | } 293 | AVPush push = new AVPush(); 294 | push.setMessage(message); 295 | push.setQuery(query); 296 | push.sendInBackground(false, null); 297 | } 298 | 299 | /** 300 | * A helper method to concisely send a push message to a query. This method is equivalent to 301 | * 302 | *
303 |    * AVPush push = new AVPush();
304 |    * push.setMessage(message);
305 |    * push.setQuery(query);
306 |    * push.sendInBackground(callback);
307 |    * 
308 | * 309 | * @param message The message that will be shown in the notification. 310 | * @param query A AVInstallation query which specifies the recipients of a push. 311 | * @param callback callback.done(e) is called when the send completes. 312 | */ 313 | public static void sendMessageInBackground(String message, AVQuery query, 314 | SendCallback callback) { 315 | if (!query.getClassName().equals(INSTALLATIONTAG)) { 316 | if (callback != null) { 317 | callback.done(new AVException(AVException.OTHER_CAUSE, "only installation query is valid")); 318 | } 319 | } 320 | AVPush push = new AVPush(); 321 | push.setMessage(message); 322 | push.setQuery(query); 323 | push.sendInBackground(false, callback); 324 | } 325 | 326 | /** 327 | * Sets the channel on which this push notification will be sent. The channel name must start with 328 | * a letter and contain only letters, numbers, dashes, and underscores. A push can either have 329 | * channels or a query. Setting this will unset the query. 330 | * 331 | * @param channel set push channel 332 | */ 333 | public void setChannel(String channel) { 334 | channelSet.clear(); 335 | channelSet.add(channel); 336 | } 337 | 338 | /** 339 | * Sets the collection of channels on which this push notification will be sent. Each channel name 340 | * must start with a letter and contain only letters, numbers, dashes, and underscores. A push can 341 | * either have channels or a query. Setting this will unset the query. 342 | * 343 | * @param channels set push channels 344 | */ 345 | public void setChannels(Collection channels) { 346 | channelSet.clear(); 347 | channelSet.addAll(channels); 348 | } 349 | 350 | /** 351 | * Sets the entire data of the push message. See the push guide for more details on the data 352 | * format. This will overwrite any data specified in AVPush.setMessage(String). 353 | * 354 | * @param data push data 355 | */ 356 | public void setData(Map data) { 357 | this.pushData.put("data", data); 358 | } 359 | 360 | /** 361 | * Sets the entire data of the push message. See the push guide for more details on the data 362 | * format. This will overwrite any data specified in AVPush.setMessage(String). 363 | * 364 | * @param data push data 365 | */ 366 | public void setData(JSONObject data) { 367 | try { 368 | Map map = new HashMap(); 369 | Iterator iter = data.keys(); 370 | while (iter.hasNext()) { 371 | String key = (String) iter.next(); 372 | Object value = data.get(key); 373 | map.put(key, value); 374 | } 375 | this.pushData.put("data", map); 376 | } catch (Exception exception) { 377 | throw new AVRuntimeException(exception); 378 | } 379 | } 380 | 381 | private Date expirationDateTime() { 382 | return new Date(expirationTime); 383 | } 384 | 385 | /** 386 | * Set the push date at which the push will be sent. 387 | * 388 | * @param date The push date. 389 | */ 390 | public void setPushDate(Date date) { 391 | this.pushDate = date; 392 | } 393 | 394 | /** 395 | * Sets a UNIX epoch timestamp at which this notification should expire, in seconds (UTC). This 396 | * notification will be sent to devices which are either online at the time the notification is 397 | * sent, or which come online before the expiration time is reached. Because device clocks are not 398 | * guaranteed to be accurate, most applications should instead use 399 | * AVPush.setExpirationTimeInterval(long). 400 | * 401 | * @param time expire date 402 | */ 403 | public void setExpirationTime(long time) { 404 | this.expirationTime = time; 405 | } 406 | 407 | /** 408 | * Sets the time interval after which this notification should expire, in seconds. This 409 | * notification will be sent to devices which are either online at the time the notification is 410 | * sent, or which come online within the given number of seconds of the notification being 411 | * received by AVOSCloud's server. An interval which is less than or equal to zero indicates that 412 | * the message should only be sent to devices which are currently online. 413 | * 414 | * @param timeInterval expirationTimeInterval 415 | */ 416 | public void setExpirationTimeInterval(long timeInterval) { 417 | this.expirationTimeInterval = timeInterval; 418 | } 419 | 420 | /** 421 | * Sets the message that will be shown in the notification. This will overwrite any data specified 422 | * in AVPush.setData(JSONObject). 423 | * 424 | * @param message set push message 425 | */ 426 | public void setMessage(String message) { 427 | pushData.clear(); 428 | Map map = AVUtils.createStringObjectMap("alert", message); 429 | pushData.put("data", map); 430 | } 431 | 432 | public void setPushToAndroid(boolean pushToAndroid) { 433 | if (pushToAndroid) { 434 | this.pushTarget.add("android"); 435 | } else { 436 | this.pushTarget.remove("android"); 437 | } 438 | } 439 | 440 | public void setPushToIOS(boolean pushToIOS) { 441 | if (pushToIOS) { 442 | this.pushTarget.add("ios"); 443 | } else { 444 | this.pushTarget.remove("ios"); 445 | } 446 | } 447 | 448 | public void setPushToWindowsPhone(boolean pushToWP) { 449 | if (pushToWP) { 450 | this.pushTarget.add("wp"); 451 | } else { 452 | this.pushTarget.remove("wp"); 453 | } 454 | } 455 | 456 | /** 457 | * Sets the query for this push for which this push notification will be sent. This query will be 458 | * executed in the AVOSCloud cloud; this push notification will be sent to Installations which 459 | * this query yields. A push can either have channels or a query. Setting this will unset the 460 | * channels. 461 | * 462 | * @param query A query to which this push should target. This must be a AVInstallation query. 463 | */ 464 | public void setQuery(AVQuery query) { 465 | this.pushQuery = query; 466 | } 467 | 468 | /** 469 | * 可以通过 cql来针对push进行筛选 470 | * 471 | * 请注意cql 的主体应该是_Installation表 472 | * 473 | * 请在设置cql的同时,不要设置pushTarget(ios,android,wp) 474 | * 475 | * @param cql cql for push 476 | * @since 2.6.7 477 | */ 478 | public void setCloudQuery(String cql) { 479 | this.cql = cql; 480 | } 481 | 482 | public boolean getProductionMode() { 483 | return this.production; 484 | } 485 | 486 | /** 487 | * 设定iOS推送是否是生产环境 488 | * 489 | * 490 | * @since 2.6.9 491 | * @param production,true为生产环境,默认是true 492 | */ 493 | public void setProductionMode(boolean production) { 494 | this.production = production; 495 | } 496 | } 497 | -------------------------------------------------------------------------------- /src/main/java/com/avos/avoscloud/internal/MasterKeyConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud.internal; 2 | 3 | public interface MasterKeyConfiguration { 4 | 5 | public String getMasterKey(); 6 | 7 | public void setMasterKey(String masterKey); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/avos/avoscloud/internal/impl/JavaAppConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud.internal.impl; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ThreadPoolExecutor; 5 | 6 | import com.avos.avoscloud.AVUtils; 7 | import com.avos.avoscloud.internal.AppConfiguration; 8 | import com.avos.avoscloud.internal.MasterKeyConfiguration; 9 | 10 | public class JavaAppConfiguration extends AppConfiguration implements MasterKeyConfiguration { 11 | 12 | String masterKey; 13 | 14 | private final String apiHookKeyField = "X-LC-Hook-Key"; 15 | 16 | private final String hookKey; 17 | 18 | public static JavaAppConfiguration instance() { 19 | synchronized (JavaAppConfiguration.class) { 20 | if (instance == null) { 21 | instance = new JavaAppConfiguration(); 22 | } 23 | } 24 | return instance; 25 | } 26 | 27 | protected JavaAppConfiguration() { 28 | hookKey = getEnvOrProperty("LEANCLOUD_APP_HOOK_KEY"); 29 | } 30 | 31 | private static JavaAppConfiguration instance; 32 | 33 | 34 | @Override 35 | public boolean isConfigured() { 36 | return !(AVUtils.isBlankString(this.getApplicationId()) 37 | || AVUtils.isBlankString(this.getClientKey()) || (JavaRequestSignImplementation.instance() 38 | .isUseMasterKey() && AVUtils.isBlankString(this.getMasterKey()))); 39 | } 40 | 41 | @Override 42 | public void setupThreadPoolExecutor(ThreadPoolExecutor excutor) { 43 | // do nothing 44 | } 45 | 46 | @Override 47 | public boolean isConnected() { 48 | return true; 49 | } 50 | 51 | @Override 52 | public String getMasterKey() { 53 | return this.masterKey; 54 | } 55 | 56 | @Override 57 | public void setMasterKey(String masterKey) { 58 | this.masterKey = masterKey; 59 | } 60 | 61 | protected String getEnvOrProperty(String key) { 62 | String value = System.getenv(key); 63 | if (value == null) { 64 | value = System.getProperty(key); 65 | } 66 | return value; 67 | } 68 | 69 | @Override 70 | public Map getRequestHeaders() { 71 | Map result = super.getRequestHeaders(); 72 | if (hookKey != null) { 73 | result.put(apiHookKeyField, hookKey); 74 | } 75 | return result; 76 | } 77 | 78 | @Override 79 | public String dumpRequestHeaders() { 80 | if (hookKey != null) { 81 | return String.format("%s -H \"%s: %s\"", super.dumpRequestHeaders(), apiHookKeyField, 82 | dumpKey(hookKey, "YourHookKey")); 83 | } 84 | return super.dumpRequestHeaders(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/avos/avoscloud/internal/impl/JavaRequestSignImplementation.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud.internal.impl; 2 | 3 | import com.avos.avoscloud.AVUtils; 4 | import com.avos.avoscloud.internal.AppConfiguration; 5 | import com.avos.avoscloud.internal.InternalConfigurationController; 6 | import com.avos.avoscloud.internal.InternalRequestSign; 7 | import com.avos.avoscloud.internal.MasterKeyConfiguration; 8 | 9 | public class JavaRequestSignImplementation implements InternalRequestSign { 10 | 11 | boolean useMasterKey = false; 12 | 13 | public static JavaRequestSignImplementation instance() { 14 | synchronized (JavaRequestSignImplementation.class) { 15 | if (instance == null) { 16 | instance = new JavaRequestSignImplementation(); 17 | } 18 | } 19 | return instance; 20 | } 21 | 22 | private JavaRequestSignImplementation() {} 23 | 24 | private static JavaRequestSignImplementation instance; 25 | 26 | @Override 27 | public String requestSign() { 28 | return requestSign(AVUtils.getCurrentTimestamp(), this.isUseMasterKey()); 29 | } 30 | 31 | public static String requestSign(long ts, boolean useMasterKey) { 32 | StringBuilder builder = new StringBuilder(); 33 | 34 | StringBuilder result = new StringBuilder(); 35 | 36 | AppConfiguration appConfiguration = 37 | InternalConfigurationController.globalInstance().getAppConfiguration(); 38 | String masterKey = null; 39 | if (appConfiguration instanceof MasterKeyConfiguration) { 40 | masterKey = ((MasterKeyConfiguration) appConfiguration).getMasterKey(); 41 | } 42 | 43 | result.append(AVUtils.md5( 44 | builder.append(ts).append(useMasterKey ? masterKey : appConfiguration.getClientKey()) 45 | .toString()).toLowerCase()); 46 | result.append(',').append(ts); 47 | if (!useMasterKey) { 48 | return result.toString(); 49 | } else { 50 | return result.append(",master").toString(); 51 | } 52 | } 53 | 54 | public void setUseMasterKey(boolean shouldUseMasterKey) { 55 | this.useMasterKey = shouldUseMasterKey; 56 | } 57 | 58 | protected boolean isUseMasterKey() { 59 | return this.useMasterKey; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/avos/avoscloud/internal/impl/Log4j2Implementation.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud.internal.impl; 2 | 3 | import com.avos.avoscloud.AVOSCloud; 4 | import com.avos.avoscloud.internal.InternalLogger; 5 | 6 | import org.apache.logging.log4j.Logger; 7 | import org.apache.logging.log4j.LogManager; 8 | 9 | public class Log4j2Implementation extends InternalLogger { 10 | 11 | private static final Logger logger = LogManager.getLogger(AVOSCloud.class); 12 | 13 | 14 | public static Log4j2Implementation instance() { 15 | synchronized (Log4j2Implementation.class) { 16 | if (instance == null) { 17 | instance = new Log4j2Implementation(); 18 | } 19 | } 20 | return instance; 21 | } 22 | 23 | private Log4j2Implementation() {} 24 | 25 | private static Log4j2Implementation instance; 26 | 27 | @Override 28 | public int v(String tag, String msg) { 29 | logger.info(msg); 30 | return 0; 31 | } 32 | 33 | @Override 34 | public int v(String tag, String msg, Throwable tr) { 35 | logger.info(msg, tr); 36 | return 0; 37 | } 38 | 39 | @Override 40 | public int d(String tag, String msg) { 41 | logger.debug(msg); 42 | return 0; 43 | } 44 | 45 | @Override 46 | public int d(String tag, String msg, Throwable tr) { 47 | logger.debug(msg, tr); 48 | return 0; 49 | } 50 | 51 | @Override 52 | public int i(String tag, String msg) { 53 | logger.info(msg); 54 | return 0; 55 | } 56 | 57 | @Override 58 | public int i(String tag, String msg, Throwable tr) { 59 | logger.info(msg, tr); 60 | return 0; 61 | } 62 | 63 | @Override 64 | public int w(String tag, String msg) { 65 | logger.warn(msg); 66 | return 0; 67 | } 68 | 69 | @Override 70 | public int w(String tag, String msg, Throwable tr) { 71 | logger.warn(msg, tr); 72 | return 0; 73 | } 74 | 75 | @Override 76 | public int w(String tag, Throwable tr) { 77 | logger.warn(tr); 78 | return 0; 79 | } 80 | 81 | @Override 82 | public int e(String tag, String msg) { 83 | logger.error(msg); 84 | return 0; 85 | } 86 | 87 | @Override 88 | public int e(String tag, String msg, Throwable tr) { 89 | logger.error(msg, tr); 90 | return 0; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/avos/avoscloud/internal/impl/SimplePersistence.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud.internal.impl; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | 10 | import com.avos.avoscloud.AVUser; 11 | import com.avos.avoscloud.AVUtils; 12 | import com.avos.avoscloud.internal.InternalPersistence; 13 | 14 | public class SimplePersistence implements InternalPersistence { 15 | 16 | public static SimplePersistence instance() { 17 | synchronized (SimplePersistence.class) { 18 | if (instance == null) { 19 | instance = new SimplePersistence(); 20 | } 21 | } 22 | return instance; 23 | } 24 | 25 | protected SimplePersistence() {} 26 | 27 | private static SimplePersistence instance; 28 | 29 | public File getPaasDocumentDir() { 30 | 31 | return null; 32 | } 33 | 34 | public File getCacheDir() { 35 | return null; 36 | } 37 | 38 | public File getCommandCacheDir() { 39 | return null; 40 | } 41 | 42 | public boolean saveContentToFile(String content, File fileForSave) { 43 | return saveContentToFile(content.getBytes(), fileForSave); 44 | } 45 | 46 | public boolean saveContentToFile(byte[] content, File fileForSave) { 47 | FileOutputStream fos = null; 48 | try { 49 | fos = new FileOutputStream(fileForSave); 50 | fos.write(content); 51 | return true; 52 | } catch (IOException e) { 53 | throw new RuntimeException(e); 54 | } finally { 55 | AVUtils.closeQuietly(fos); 56 | } 57 | } 58 | 59 | public void saveToDocumentDir(String content, String folderName, String fileName) { 60 | saveContentToFile(content.getBytes(), new File(folderName, fileName)); 61 | } 62 | 63 | public String getFromDocumentDir(String folderName, String fileName) { 64 | return readContentFromFile(new File(folderName, fileName)); 65 | } 66 | 67 | public String readContentFromFile(File fileForRead) { 68 | return new String(readContentBytesFromFile(fileForRead)); 69 | } 70 | 71 | public byte[] readContentBytesFromFile(File fileForRead) { 72 | byte[] buffer = null; 73 | FileInputStream fis = null; 74 | ByteArrayOutputStream bos = null; 75 | try { 76 | fis = new FileInputStream(fileForRead); 77 | bos = new ByteArrayOutputStream(); 78 | byte[] b = new byte[1024 * 5]; 79 | int n; 80 | while ((n = fis.read(b)) != -1) { 81 | bos.write(b, 0, n); 82 | } 83 | buffer = bos.toByteArray(); 84 | } catch (FileNotFoundException e) { 85 | throw new RuntimeException(e); 86 | } catch (IOException e) { 87 | throw new RuntimeException(e); 88 | } finally { 89 | AVUtils.closeQuietly(fis); 90 | AVUtils.closeQuietly(bos); 91 | } 92 | return buffer; 93 | } 94 | 95 | public void deleteFile(File file) { 96 | try { 97 | file.deleteOnExit(); 98 | } catch (SecurityException e) { 99 | throw new RuntimeException(e); 100 | } 101 | } 102 | 103 | public void savePersistentSettingBoolean(String keyzone, String key, Boolean value) { 104 | 105 | } 106 | 107 | public boolean getPersistentSettingBoolean(String keyzone, String key) { 108 | // TODO Auto-generated method stub 109 | return false; 110 | } 111 | 112 | public boolean getPersistentSettingBoolean(String keyzone, String key, Boolean defaultValue) { 113 | // TODO Auto-generated method stub 114 | return defaultValue; 115 | } 116 | 117 | public void savePersistentSettingInteger(String keyzone, String key, Integer value) { 118 | // TODO Auto-generated method stub 119 | 120 | } 121 | 122 | public Integer getPersistentSettingInteger(String keyzone, String key, Integer defaultValue) { 123 | // TODO Auto-generated method stub 124 | return defaultValue; 125 | } 126 | 127 | public Long getPersistentSettingLong(String keyzone, String key, Long defaultValue) { 128 | // TODO Auto-generated method stub 129 | return defaultValue; 130 | } 131 | 132 | public void savePersistentSettingLong(String keyzone, String key, Long value) { 133 | // TODO Auto-generated method stub 134 | 135 | } 136 | 137 | public void savePersistentSettingString(String keyzone, String key, String value) { 138 | 139 | } 140 | 141 | public String getPersistentSettingString(String keyzone, String key, String defaultValue) { 142 | return defaultValue; 143 | } 144 | 145 | public void removePersistentSettingString(String keyzone, String key) { 146 | 147 | } 148 | 149 | public String removePersistentSettingString(String keyzone, String key, String defaultValue) { 150 | return null; 151 | } 152 | 153 | public void removeKeyZonePersistentSettings(String keyzone) { 154 | 155 | } 156 | 157 | public String getAVFileCachePath() { 158 | // TODO Auto-generated method stub 159 | return null; 160 | } 161 | 162 | public File getAVFileCacheFile(String url) { 163 | // TODO Auto-generated method stub 164 | return null; 165 | } 166 | 167 | public void cleanAVFileCache(int days) { 168 | // TODO Auto-generated method stub 169 | 170 | } 171 | 172 | private AVUser currentUser; 173 | 174 | public void setCurrentUser(AVUser user, boolean clean) { 175 | this.currentUser = user; 176 | } 177 | 178 | public T getCurrentUser(Class userClass) { 179 | if (currentUser != null) { 180 | return (T) AVUser.cast(currentUser, userClass); 181 | } else { 182 | return null; 183 | } 184 | 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/test/java/com/avos/avoscloud/DisableHookTest.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud; 2 | 3 | import java.util.Arrays; 4 | 5 | import junit.framework.Test; 6 | import junit.framework.TestCase; 7 | import junit.framework.TestSuite; 8 | 9 | public class DisableHookTest extends TestCase { 10 | 11 | public DisableHookTest(String testName) { 12 | super(testName); 13 | } 14 | 15 | @Override 16 | public void setUp() { 17 | TestApp.init(); 18 | } 19 | 20 | /** 21 | * @return the suite of tests being tested 22 | */ 23 | public static Test suite() { 24 | return new TestSuite(DisableHookTest.class); 25 | } 26 | 27 | public void testSaveHook() throws AVException { 28 | AVObject object = new AVObject("IgnoreHookTest"); 29 | object.put("title", "test"); 30 | object.save(); 31 | object.fetch(); 32 | assertEquals(1, object.getInt("byBeforeSave")); 33 | assertEquals(1, object.getInt("byAfterSave")); 34 | } 35 | 36 | public void testSaveHook_disableBeforeHook() throws AVException { 37 | AVObject object = new AVObject("IgnoreHookTest"); 38 | object.put("title", "test"); 39 | object.disableBeforeHook(); 40 | object.save(); 41 | object.fetch(); 42 | assertNull(object.get("byBeforeSave")); 43 | assertEquals(1, object.getInt("byAfterSave")); 44 | } 45 | 46 | public void testSaveHook_disableAfterHook() throws AVException { 47 | AVObject object = new AVObject("IgnoreHookTest"); 48 | object.put("title", "test"); 49 | object.disableAfterHook(); 50 | object.save(); 51 | object.fetch(); 52 | assertEquals(1, object.getInt("byBeforeSave")); 53 | assertNull(object.get("byAfterSave")); 54 | } 55 | 56 | public void testUpdateHook() throws AVException { 57 | AVObject object = new AVObject("IgnoreHookTest"); 58 | object.put("title", "test"); 59 | object.save(); 60 | object.put("title", "something"); 61 | object.save(); 62 | object.fetch(); 63 | assertEquals(1, object.getInt("byBeforeSave")); 64 | assertEquals(1, object.getInt("byAfterSave")); 65 | assertEquals(1, object.getInt("byBeforeUpdate")); 66 | assertEquals(1, object.getInt("byAfterUpdate")); 67 | } 68 | 69 | public void testUpdateHook_disableBeforeHook() throws AVException { 70 | AVObject object = new AVObject("IgnoreHookTest"); 71 | object.put("title", "test"); 72 | object.save(); 73 | object.put("title", "something"); 74 | object.disableBeforeHook(); 75 | object.save(); 76 | object.fetch(); 77 | assertEquals(1, object.getInt("byBeforeSave")); 78 | assertEquals(1, object.getInt("byAfterSave")); 79 | assertNull(object.get("byBeforeUpdate")); 80 | assertEquals(1, object.getInt("byAfterUpdate")); 81 | } 82 | 83 | public void testUpdateHook_disableAfterHook() throws AVException { 84 | AVObject object = new AVObject("IgnoreHookTest"); 85 | object.put("title", "test"); 86 | object.save(); 87 | object.put("title", "something"); 88 | object.disableAfterHook(); 89 | object.save(); 90 | object.fetch(); 91 | assertEquals(1, object.getInt("byBeforeSave")); 92 | assertEquals(1, object.getInt("byAfterSave")); 93 | assertEquals(1, object.getInt("byBeforeUpdate")); 94 | assertNull(object.get("byAfterUpdate")); 95 | } 96 | 97 | public void testHook_haveChildren_disableHook() throws AVException { 98 | AVObject child1 = new AVObject("IgnoreHookTest"); 99 | child1.put("title", "child1"); 100 | child1.disableBeforeHook(); 101 | 102 | AVObject object = new AVObject("IgnoreHookTest"); 103 | object.put("title", "test"); 104 | object.disableAfterHook(); 105 | 106 | object.put("child1", child1); 107 | 108 | object.save(); 109 | 110 | object.fetch(); 111 | assertEquals(1, object.getInt("byBeforeSave")); 112 | assertNull(object.get("byAfterSave")); 113 | 114 | child1.fetch(); 115 | assertNull(child1.get("byBeforeSave")); 116 | assertEquals(1, child1.getInt("byAfterSave")); 117 | 118 | child1.put("title", "child1 something"); 119 | object.put("child1", child1); 120 | 121 | AVObject child2 = new AVObject("IgnoreHookTest"); 122 | child2.put("title", "child2"); 123 | child2.disableAfterHook(); 124 | 125 | object.put("child2", child2); 126 | 127 | object.save(); 128 | 129 | object.fetch(); 130 | assertEquals(1, object.getInt("byBeforeUpdate")); 131 | assertNull(object.get("byAfterUpdate")); 132 | 133 | child1.fetch(); 134 | assertNull(child1.get("byBeforeUpdate")); 135 | assertEquals(1, child1.getInt("byAfterUpdate")); 136 | 137 | child2.fetch(); 138 | assertEquals(1, child2.getInt("byBeforeSave")); 139 | assertNull(child2.get("byAfterSave")); 140 | } 141 | 142 | public void testDeleteHook_disableBeforeHook() throws AVException { 143 | AVObject object = new AVObject("IgnoreHookTest"); 144 | object.put("title", "test"); 145 | object.save(); 146 | object.fetch(); 147 | object.put("title", "something"); 148 | object.disableBeforeHook(); 149 | object.delete(); 150 | } 151 | 152 | public void testDeleteHook_deleteAll() throws AVException { 153 | AVObject object = new AVObject("IgnoreHookTest"); 154 | object.put("title", "object1"); 155 | object.save(); 156 | 157 | try { 158 | AVObject.deleteAll(Arrays.asList(object)); 159 | fail("should throw 'Cloud Code vaildation failed' exception."); 160 | } catch (AVException e) { 161 | assertTrue(e.getMessage() 162 | .startsWith("Cloud Code validation failed. Error detail : Error from beforeDelete")); 163 | } 164 | 165 | AVObject object1 = new AVObject("IgnoreHookTest"); 166 | object1.put("title", "object1"); 167 | object1.ignoreHook(AVObject.Hook.beforeDelete); 168 | object1.save(); 169 | 170 | AVObject object2 = new AVObject("IgnoreHookTest"); 171 | object2.put("title", "object2"); 172 | object2.disableBeforeHook(); 173 | object2.save(); 174 | 175 | AVObject object3 = new AVObject("IgnoreHookTest"); 176 | object3.put("title", "object2"); 177 | object3.disableBeforeHook(); 178 | object3.save(); 179 | 180 | AVObject.deleteAll(Arrays.asList(object1, object2, object3)); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/test/java/com/avos/avoscloud/TestApp.java: -------------------------------------------------------------------------------- 1 | package com.avos.avoscloud; 2 | 3 | import com.avos.avoscloud.internal.InternalConfigurationController; 4 | import com.avos.avoscloud.internal.InternalLogger; 5 | import com.avos.avoscloud.internal.impl.DefaultAppRouter; 6 | import com.avos.avoscloud.internal.impl.JavaAppConfiguration; 7 | import com.avos.avoscloud.internal.impl.Log4j2Implementation; 8 | import com.avos.avoscloud.internal.impl.SimplePersistence; 9 | 10 | public class TestApp { 11 | 12 | public static void init() { 13 | TestApp.init("uu2P5gNTxGhjyaJGAPPnjCtJ-gzGzoHsz", "j5lErUd6q7LhPD8CXhfmA2Rg", 14 | "atXAmIVlQoBDBLqumMgzXhcY", "rTXH9hotZfAJjtFOQqwfEHpC", true); 15 | } 16 | 17 | public static void init(String applicationId, String clientKey, String masterKey, String hookKey, 18 | boolean isCN) { 19 | System.setProperty("LEANCLOUD_APP_HOOK_KEY", hookKey); 20 | JavaAppConfiguration configuration = JavaAppConfiguration.instance(); 21 | configuration.setIsCN(isCN); 22 | configuration.setApplicationId(applicationId); 23 | configuration.setClientKey(clientKey); 24 | configuration.setMasterKey(masterKey); 25 | 26 | InternalLogger logger = Log4j2Implementation.instance(); 27 | logger.setDebugEnabled(true); 28 | 29 | new InternalConfigurationController.Builder().setAppConfiguration(configuration) 30 | .setAppRouter(DefaultAppRouter.instance()).setInternalLogger(logger) 31 | .setInternalPersistence(SimplePersistence.instance()) 32 | .build(); 33 | InternalConfigurationController.globalInstance().getAppRouter().updateServerHosts(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------