diff --git a/.gitignore b/.gitignore index 08c431f6..b7aba910 100755 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ hs_err_pid* .idea target - +.iml diff --git a/common/common.iml b/common/common.iml new file mode 100644 index 00000000..746b44bf --- /dev/null +++ b/common/common.iml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/manager/src/main/java/cc/iotkit/manager/config/Constants.java b/common/src/main/java/cc/iotkit/common/Constants.java old mode 100755 new mode 100644 similarity index 97% rename from manager/src/main/java/cc/iotkit/manager/config/Constants.java rename to common/src/main/java/cc/iotkit/common/Constants.java index 83aa6722..f4a1987d --- a/manager/src/main/java/cc/iotkit/manager/config/Constants.java +++ b/common/src/main/java/cc/iotkit/common/Constants.java @@ -1,4 +1,4 @@ -package cc.iotkit.manager.config; +package cc.iotkit.common; public interface Constants { @@ -6,16 +6,16 @@ public interface Constants { String ACCOUNT_SECRET = "3n1z33kzvpgz1foijpkepyd3e8tw84us"; - String WECHAT_APP_ID = "wx791cb7bf75950e0c"; - - String WECHAT_APP_SECRET = "eeef73ce71f1a722ad6298985d859844"; - String PRODUCT_CACHE = "product_cache"; String DEVICE_CACHE = "device_cache"; String THING_MODEL_CACHE = "thing_model_cache"; + String WECHAT_APP_ID = "wx791cb7bf75950e0c"; + + String WECHAT_APP_SECRET = "eeef73ce71f1a722ad6298985d859844"; + String APP_DESIGN_CACHE = "app_design_cache"; /** diff --git a/dao/dao.iml b/dao/dao.iml new file mode 100644 index 00000000..178954d8 --- /dev/null +++ b/dao/dao.iml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dao/src/main/java/cc/iotkit/dao/Constants.java b/dao/src/main/java/cc/iotkit/dao/Constants.java deleted file mode 100755 index 1a81a38e..00000000 --- a/dao/src/main/java/cc/iotkit/dao/Constants.java +++ /dev/null @@ -1,11 +0,0 @@ -package cc.iotkit.dao; - -public interface Constants { - - String PRODUCT_CACHE = "product_cache"; - - String DEVICE_CACHE = "device_cache"; - - String THING_MODEL_CACHE = "thing_model_cache"; - -} diff --git a/dao/src/main/java/cc/iotkit/dao/DeviceDao.java b/dao/src/main/java/cc/iotkit/dao/DeviceDao.java index 4f9058c1..1d34e2b9 100755 --- a/dao/src/main/java/cc/iotkit/dao/DeviceDao.java +++ b/dao/src/main/java/cc/iotkit/dao/DeviceDao.java @@ -1,5 +1,6 @@ package cc.iotkit.dao; +import cc.iotkit.common.Constants; import cc.iotkit.model.device.DeviceInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; diff --git a/dao/src/main/java/cc/iotkit/dao/ProductDao.java b/dao/src/main/java/cc/iotkit/dao/ProductDao.java index da3d1b42..18bd810f 100755 --- a/dao/src/main/java/cc/iotkit/dao/ProductDao.java +++ b/dao/src/main/java/cc/iotkit/dao/ProductDao.java @@ -1,5 +1,6 @@ package cc.iotkit.dao; +import cc.iotkit.common.Constants; import cc.iotkit.model.product.Product; import cc.iotkit.model.product.ThingModel; import org.springframework.beans.factory.annotation.Autowired; diff --git a/device-server/device-api/device-api.iml b/device-server/device-api/device-api.iml new file mode 100644 index 00000000..d18ce86d --- /dev/null +++ b/device-server/device-api/device-api.iml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/device-server/device-server.iml b/device-server/device-server.iml new file mode 100644 index 00000000..f409c0ea --- /dev/null +++ b/device-server/device-server.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/device-server/mqtt-auth/mqtt-auth.iml b/device-server/mqtt-auth/mqtt-auth.iml new file mode 100644 index 00000000..10abb7d1 --- /dev/null +++ b/device-server/mqtt-auth/mqtt-auth.iml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/config/Constants.java b/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/config/Constants.java deleted file mode 100755 index c6c50ae0..00000000 --- a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/config/Constants.java +++ /dev/null @@ -1,9 +0,0 @@ -package cc.iotkit.mqttauth.config; - -public interface Constants { - - String MQTT_SECRET = "xdkKUymrEGSCYWswqCvSPyRSFvH5j7CU"; - - String ACCOUNT_SECRET = "3n1z33kzvpgz1foijpkepyd3e8tw84us"; - -} diff --git a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/controller/MqttAuthController.java b/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/controller/MqttAuthController.java index 9dacf3e8..3806f20e 100755 --- a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/controller/MqttAuthController.java +++ b/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/controller/MqttAuthController.java @@ -1,9 +1,9 @@ package cc.iotkit.mqttauth.controller; +import cc.iotkit.common.Constants; import cc.iotkit.common.utils.CodecUtil; import cc.iotkit.common.utils.JsonUtil; -import cc.iotkit.mqttauth.config.Constants; import cc.iotkit.mqttauth.model.EmqAcl; import cc.iotkit.mqttauth.model.EmqAuthInfo; import cc.iotkit.mqttauth.service.DeviceMqttAuth; diff --git a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/DeviceMqttAuth.java b/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/DeviceMqttAuth.java index bc7a9b49..cfffe3ff 100755 --- a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/DeviceMqttAuth.java +++ b/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/DeviceMqttAuth.java @@ -1,7 +1,7 @@ package cc.iotkit.mqttauth.service; +import cc.iotkit.common.Constants; import cc.iotkit.model.device.DeviceInfo; -import cc.iotkit.mqttauth.config.Constants; import cc.iotkit.mqttauth.model.EmqAcl; import cc.iotkit.mqttauth.model.EmqAuthInfo; import lombok.extern.slf4j.Slf4j; diff --git a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/SysMqttAuth.java b/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/SysMqttAuth.java index 5f5d332c..450eadf8 100755 --- a/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/SysMqttAuth.java +++ b/device-server/mqtt-auth/src/main/java/cc/iotkit/mqttauth/service/SysMqttAuth.java @@ -1,7 +1,7 @@ package cc.iotkit.mqttauth.service; +import cc.iotkit.common.Constants; import cc.iotkit.common.utils.CodecUtil; -import cc.iotkit.mqttauth.config.Constants; import cc.iotkit.mqttauth.model.EmqAcl; import cc.iotkit.mqttauth.model.EmqAuthInfo; import lombok.extern.slf4j.Slf4j; diff --git a/device-server/mqtt-auth/src/test/java/SupperUser.java b/device-server/mqtt-auth/src/test/java/SupperUser.java index bd0f1c16..40e2f115 100755 --- a/device-server/mqtt-auth/src/test/java/SupperUser.java +++ b/device-server/mqtt-auth/src/test/java/SupperUser.java @@ -1,22 +1,14 @@ import cc.iotkit.common.utils.CodecUtil; import cc.iotkit.mqttauth.config.Constants; import cc.iotkit.mqttauth.controller.MqttAuthController; -import org.apache.commons.codec.digest.DigestUtils; import org.junit.Assert; import org.junit.Test; public class SupperUser { - public static void main(String[] args) throws Exception { - System.out.println("clientId:su_" + CodecUtil.aesEncrypt("admin_produce_dev", Constants.MQTT_SECRET)); - - String hmac = DigestUtils.md5Hex(Constants.MQTT_SECRET + "2P6MDKr8cB7y8EmM_ABC123DEF456"); - System.out.println(hmac); - - } - @Test public void createSuperuser() throws Exception { + //mqtt生成超级用户,作为mqtt-server连接mqtt的clientId String clientId = "mqtt-server-producer-dev"; System.out.println("clientId:su_" + CodecUtil.aesEncrypt("admin_" + clientId, Constants.MQTT_SECRET)); } diff --git a/device-server/mqtt-server/mqtt-server.iml b/device-server/mqtt-server/mqtt-server.iml new file mode 100644 index 00000000..0aac775c --- /dev/null +++ b/device-server/mqtt-server/mqtt-server.iml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/device-server/mqtt-server/src/main/java/cc/iotkit/server/config/Constants.java b/device-server/mqtt-server/src/main/java/cc/iotkit/server/config/Constants.java deleted file mode 100755 index 53603656..00000000 --- a/device-server/mqtt-server/src/main/java/cc/iotkit/server/config/Constants.java +++ /dev/null @@ -1,25 +0,0 @@ -package cc.iotkit.server.config; - -public interface Constants { - - String MQTT_SECRET = "xdkKUymrEGSCYWswqCvSPyRSFvH5j7CU"; - - String ACCOUNT_SECRET = "3n1z33kzvpgz1foijpkepyd3e8tw84us"; - - String PRODUCT_CACHE = "product_cache"; - - String DEVICE_CACHE = "device_cache"; - - String THING_MODEL_CACHE = "thing_model_cache"; - - /** - * topic前缀第三方接入网关 - */ - String TOPIC_PREFIX_GATEWAY = "gateway"; - - /** - * topic前缀APP - */ - String TOPIC_PREFIX_APP = "app"; - -} diff --git a/device-server/mqtt-server/src/main/java/cc/iotkit/server/config/MqttConfig.java b/device-server/mqtt-server/src/main/java/cc/iotkit/server/config/MqttConfig.java index e7bf827d..bdf661ed 100755 --- a/device-server/mqtt-server/src/main/java/cc/iotkit/server/config/MqttConfig.java +++ b/device-server/mqtt-server/src/main/java/cc/iotkit/server/config/MqttConfig.java @@ -1,6 +1,9 @@ package cc.iotkit.server.config; +import cc.iotkit.common.Constants; +import cc.iotkit.common.utils.CodecUtil; import cc.iotkit.server.handler.MqttConsumerHandler; +import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.springframework.beans.factory.annotation.Value; @@ -29,26 +32,11 @@ public class MqttConfig { */ public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel"; - @Value("${mqtt.username}") - private String username; - - @Value("${mqtt.password}") - private String password; - @Value("${mqtt.url}") private String url; - @Value("${mqtt.producer.clientId}") - private String producerClientId; - - @Value("${mqtt.producer.defaultTopic}") - private String producerDefaultTopic; - - @Value("${mqtt.consumer.clientId}") - private String consumerClientId; - - @Value("${mqtt.consumer.defaultTopic}") - private String consumerDefaultTopic; + @Value(("${spring.profiles.active}")) + private String env; /** * MQTT连接器选项 @@ -62,9 +50,9 @@ public class MqttConfig { // 这里设置为true表示每次连接到服务器都以新的身份连接 options.setCleanSession(true); // 设置连接的用户名 - options.setUserName(username); + options.setUserName("admin"); // 设置连接的密码 - options.setPassword(password.toCharArray()); + options.setPassword("password".toCharArray()); options.setServerURIs(StringUtils.split(url, ",")); // 设置超时时间 单位为秒 options.setConnectionTimeout(10); @@ -100,14 +88,16 @@ public class MqttConfig { * * @return {@link MessageHandler} */ + @SneakyThrows @Bean @ServiceActivator(inputChannel = CHANNEL_NAME_OUT) public MessageHandler mqttOutbound() { + String clientId = "mqtt-server-producer-" + env; + clientId = "su_" + CodecUtil.aesEncrypt("admin_" + clientId, Constants.MQTT_SECRET); MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler( - producerClientId, + clientId, mqttClientFactory()); messageHandler.setAsync(true); - messageHandler.setDefaultTopic(producerDefaultTopic); return messageHandler; } @@ -116,13 +106,16 @@ public class MqttConfig { * * @return {@link MessageProducer} */ + @SneakyThrows @Bean public MessageProducer inbound() { // 可以同时消费(订阅)多个Topic + String clientId = "mqtt-server-consumer-" + env; + clientId = "su_" + CodecUtil.aesEncrypt("admin_" + clientId, Constants.MQTT_SECRET); MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( - consumerClientId, mqttClientFactory(), - StringUtils.split(consumerDefaultTopic, ",")); + clientId, mqttClientFactory(), + "/sys/#"); adapter.setCompletionTimeout(5000); adapter.setConverter(new DefaultPahoMessageConverter()); adapter.setQos(1); diff --git a/device-server/mqtt-server/src/main/java/cc/iotkit/server/dao/DeviceDao.java b/device-server/mqtt-server/src/main/java/cc/iotkit/server/dao/DeviceDao.java index 7d88dfdd..e553f4d2 100755 --- a/device-server/mqtt-server/src/main/java/cc/iotkit/server/dao/DeviceDao.java +++ b/device-server/mqtt-server/src/main/java/cc/iotkit/server/dao/DeviceDao.java @@ -1,7 +1,7 @@ package cc.iotkit.server.dao; +import cc.iotkit.common.Constants; import cc.iotkit.model.device.DeviceInfo; -import cc.iotkit.server.config.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.mongodb.core.MongoTemplate; diff --git a/device-server/mqtt-server/src/main/java/cc/iotkit/server/handler/MqttConsumerHandler.java b/device-server/mqtt-server/src/main/java/cc/iotkit/server/handler/MqttConsumerHandler.java index 8e8f5dc7..49a45cc3 100755 --- a/device-server/mqtt-server/src/main/java/cc/iotkit/server/handler/MqttConsumerHandler.java +++ b/device-server/mqtt-server/src/main/java/cc/iotkit/server/handler/MqttConsumerHandler.java @@ -1,7 +1,7 @@ package cc.iotkit.server.handler; +import cc.iotkit.common.Constants; import cc.iotkit.common.utils.JsonUtil; -import cc.iotkit.server.config.Constants; import cc.iotkit.server.dao.DeviceDao; import cc.iotkit.model.device.DeviceInfo; import cc.iotkit.model.mq.Request; diff --git a/device-server/mqtt-server/src/main/resources/application-dev.yml b/device-server/mqtt-server/src/main/resources/application-dev.yml index a28f0e65..82350b33 100755 --- a/device-server/mqtt-server/src/main/resources/application-dev.yml +++ b/device-server/mqtt-server/src/main/resources/application-dev.yml @@ -10,17 +10,5 @@ spring: spec: maximumSize=5000,expireAfterAccess=120s mqtt: - username: admin - password: password url: tcp://填写mqtt连接地址 - producer: - #su_mqtt-server-producer-prod - clientId: 填写mqtt连接clientId见文档中生成clientId说明 - defaultTopic: topic1 - - consumer: - #su_mqtt-server-consumer-prod - clientId: 填写mqtt连接clientId见文档中生成clientId说明 - defaultTopic: /sys/# - diff --git a/device-server/mqtt-server/src/main/resources/application.yml b/device-server/mqtt-server/src/main/resources/application.yml index a28f0e65..84c91757 100755 --- a/device-server/mqtt-server/src/main/resources/application.yml +++ b/device-server/mqtt-server/src/main/resources/application.yml @@ -10,17 +10,4 @@ spring: spec: maximumSize=5000,expireAfterAccess=120s mqtt: - username: admin - password: password url: tcp://填写mqtt连接地址 - - producer: - #su_mqtt-server-producer-prod - clientId: 填写mqtt连接clientId见文档中生成clientId说明 - defaultTopic: topic1 - - consumer: - #su_mqtt-server-consumer-prod - clientId: 填写mqtt连接clientId见文档中生成clientId说明 - defaultTopic: /sys/# - diff --git a/iotkit-parent.iml b/iotkit-parent.iml new file mode 100644 index 00000000..f409c0ea --- /dev/null +++ b/iotkit-parent.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/manager/manager.iml b/manager/manager.iml new file mode 100644 index 00000000..da8de8e6 --- /dev/null +++ b/manager/manager.iml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/manager/src/main/java/cc/iotkit/manager/controller/SystemController.java b/manager/src/main/java/cc/iotkit/manager/controller/SystemController.java index a89cf8c8..8b491d35 100755 --- a/manager/src/main/java/cc/iotkit/manager/controller/SystemController.java +++ b/manager/src/main/java/cc/iotkit/manager/controller/SystemController.java @@ -1,8 +1,8 @@ package cc.iotkit.manager.controller; +import cc.iotkit.common.Constants; import cc.iotkit.common.utils.CodecUtil; import cc.iotkit.dao.UserAccountRepository; -import cc.iotkit.manager.config.Constants; import cc.iotkit.model.UserAccount; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; diff --git a/manager/src/main/java/cc/iotkit/manager/controller/UserInfoController.java b/manager/src/main/java/cc/iotkit/manager/controller/UserInfoController.java index 4d795040..4cb24426 100755 --- a/manager/src/main/java/cc/iotkit/manager/controller/UserInfoController.java +++ b/manager/src/main/java/cc/iotkit/manager/controller/UserInfoController.java @@ -1,9 +1,9 @@ package cc.iotkit.manager.controller; +import cc.iotkit.common.Constants; import cc.iotkit.common.exception.BizException; import cc.iotkit.common.utils.ReflectUtil; import cc.iotkit.dao.UserInfoRepository; -import cc.iotkit.manager.config.Constants; import cc.iotkit.manager.service.AligenieService; import cc.iotkit.manager.service.KeycloakAdminService; import cc.iotkit.manager.utils.AuthUtil; diff --git a/manager/src/main/java/cc/iotkit/manager/service/AccountService.java b/manager/src/main/java/cc/iotkit/manager/service/AccountService.java index 9bd69bf3..b1815d24 100755 --- a/manager/src/main/java/cc/iotkit/manager/service/AccountService.java +++ b/manager/src/main/java/cc/iotkit/manager/service/AccountService.java @@ -1,9 +1,9 @@ package cc.iotkit.manager.service; +import cc.iotkit.common.Constants; import cc.iotkit.common.utils.CodecUtil; import cc.iotkit.dao.UserAccountRepository; import cc.iotkit.dao.UserInfoRepository; -import cc.iotkit.manager.config.Constants; import cc.iotkit.model.UserAccount; import cc.iotkit.model.UserInfo; import lombok.SneakyThrows; diff --git a/manager/src/main/java/cc/iotkit/manager/service/AppDesignService.java b/manager/src/main/java/cc/iotkit/manager/service/AppDesignService.java index 640d706e..91ee3191 100755 --- a/manager/src/main/java/cc/iotkit/manager/service/AppDesignService.java +++ b/manager/src/main/java/cc/iotkit/manager/service/AppDesignService.java @@ -1,8 +1,8 @@ package cc.iotkit.manager.service; +import cc.iotkit.common.Constants; import cc.iotkit.common.utils.JsonUtil; import cc.iotkit.dao.AppDesignRepository; -import cc.iotkit.manager.config.Constants; import cc.iotkit.manager.model.vo.AppPageNode; import cc.iotkit.model.product.AppDesign; import com.fasterxml.jackson.databind.JsonNode; diff --git a/manager/src/main/java/cc/iotkit/manager/service/WeChatService.java b/manager/src/main/java/cc/iotkit/manager/service/WeChatService.java index 1d1b1323..eff3d73a 100755 --- a/manager/src/main/java/cc/iotkit/manager/service/WeChatService.java +++ b/manager/src/main/java/cc/iotkit/manager/service/WeChatService.java @@ -1,9 +1,9 @@ package cc.iotkit.manager.service; +import cc.iotkit.common.Constants; import cc.iotkit.common.utils.CodecUtil; import cc.iotkit.common.utils.JsonUtil; import cc.iotkit.dao.UserInfoRepository; -import cc.iotkit.manager.config.Constants; import cc.iotkit.manager.utils.WeChatUtil; import cc.iotkit.model.UserInfo; import lombok.Data; diff --git a/manager/src/main/java/cc/iotkit/manager/utils/AuthUtil.java b/manager/src/main/java/cc/iotkit/manager/utils/AuthUtil.java index 9d94a38b..0a1d889d 100755 --- a/manager/src/main/java/cc/iotkit/manager/utils/AuthUtil.java +++ b/manager/src/main/java/cc/iotkit/manager/utils/AuthUtil.java @@ -1,6 +1,6 @@ package cc.iotkit.manager.utils; -import cc.iotkit.manager.config.Constants; +import cc.iotkit.common.Constants; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; diff --git a/model/model.iml b/model/model.iml new file mode 100644 index 00000000..c1020311 --- /dev/null +++ b/model/model.iml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rule-engine/rule-engine.iml b/rule-engine/rule-engine.iml new file mode 100644 index 00000000..1934707f --- /dev/null +++ b/rule-engine/rule-engine.iml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tppa-server/tppa-server.iml b/tppa-server/tppa-server.iml new file mode 100644 index 00000000..92b3c9e3 --- /dev/null +++ b/tppa-server/tppa-server.iml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file