├── release ├── bin │ ├── server.cmd │ └── server └── config │ ├── bridge.yml │ ├── model.json │ └── logback.xml ├── src ├── test │ ├── resources │ │ ├── send.properties │ │ └── table.sql │ └── java │ │ └── com │ │ └── github │ │ └── shicloud │ │ └── bridge │ │ ├── Test.java │ │ ├── BridgeSendTest.java │ │ └── BridgeSendComplexTest.java └── main │ └── java │ └── com │ └── github │ └── shicloud │ └── bridge │ ├── annotation │ ├── RefValue.java │ ├── StaticValue.java │ └── Subscribe.java │ ├── controller │ └── SystemController.java │ ├── config │ ├── BridgeConfig.java │ └── MqttConfig.java │ ├── model │ ├── Model.java │ ├── Field.java │ └── ModelParser.java │ ├── Startup.java │ └── mysql │ └── MsgHandler.java ├── .gitignore ├── README_EN.md ├── README.md ├── pom.xml └── LICENSE /release/bin/server.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -Dloader.path="../" -jar ../iot-mqtt-bridge-1.0.jar --spring.config.location=../config/bridge.yml 3 | 4 | -------------------------------------------------------------------------------- /release/bin/server: -------------------------------------------------------------------------------- 1 | java -Dloader.path="$(dirname "$PWD")/" -jar $(dirname "$PWD")/iot-mqtt-bridge-1.0.jar --spring.config.location=$(dirname "$PWD")/config/bridge.yml 2 | -------------------------------------------------------------------------------- /src/test/resources/send.properties: -------------------------------------------------------------------------------- 1 | url=tcp://192.168.56.101:1883 2 | username=admin 3 | password=public 4 | keepAlive=20 5 | retained=false 6 | reconnectAttemptsMax=-1 7 | reconnectDelay=10 8 | logBackXmlPath=C:/workspace5/iot-mqtt-bridge/src/main/resources/logback.xml 9 | -------------------------------------------------------------------------------- /src/test/resources/table.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS t_device_info; 2 | CREATE TABLE t_device_info ( 3 | id int AUTO_INCREMENT, 4 | device_id smallint NOT NULL, 5 | msg_code int, 6 | msg_value int, 7 | time_stamp datetime, 8 | PRIMARY KEY (id) 9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/annotation/RefValue.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.annotation; 2 | 3 | import static java.lang.annotation.ElementType.TYPE; 4 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * @author sfilo 11 | * 12 | */ 13 | @Target({ TYPE }) 14 | @Retention(RUNTIME) 15 | public @interface RefValue { 16 | /** 17 | * target 18 | */ 19 | String target() default ""; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/annotation/StaticValue.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.annotation; 2 | 3 | import static java.lang.annotation.ElementType.TYPE; 4 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * @author sfilo 11 | * 12 | */ 13 | @Target({ TYPE }) 14 | @Retention(RUNTIME) 15 | public @interface StaticValue { 16 | /** 17 | * name 18 | */ 19 | String name() default ""; 20 | } 21 | -------------------------------------------------------------------------------- /release/config/bridge.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | driver-class-name: com.mysql.jdbc.Driver 4 | url: jdbc:mysql://localhost:3306/mqtt_test?useUnicode=true&characterEncoding=utf-8&useSSL=false 5 | username: 6 | password: 7 | logging: 8 | config: ../config/logback.xml 9 | bridge: 10 | modelPath: ../config/model.json 11 | mqtts: 12 | - url: tcp://localhost:1883 13 | username: 14 | password: 15 | keepAlive: 20 16 | retained: false 17 | reconnectAttemptsMax: -1 18 | reconnectDelay: 10 19 | models: 20 | - user 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/controller/SystemController.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.controller; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | public class SystemController { 10 | 11 | private static Logger log = LoggerFactory.getLogger(SystemController.class); 12 | 13 | @RequestMapping(value = "/ping") 14 | public String ping() { 15 | log.debug("ping"); 16 | return "ok"; 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | /target/ 25 | /.classpath 26 | /.project 27 | /.settings/org.eclipse.core.resources.prefs 28 | /.settings/org.eclipse.jdt.core.prefs 29 | /.settings/org.eclipse.m2e.core.prefs 30 | /src/main/resources/bridge.yml 31 | /src/main/resources/logback.xml 32 | /src/main/resources/model.json 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/annotation/Subscribe.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.annotation; 2 | 3 | import static java.lang.annotation.ElementType.TYPE; 4 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * @author sfilo 11 | * 12 | */ 13 | @Target({ TYPE }) 14 | @Retention(RUNTIME) 15 | public @interface Subscribe { 16 | /** 17 | * topic 18 | */ 19 | String topic() default ""; 20 | 21 | /** 22 | * qos 23 | */ 24 | int qos() default 0; 25 | 26 | /** 27 | * storeType 28 | */ 29 | String storeType() default ""; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/github/shicloud/bridge/Test.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge; 2 | 3 | import com.github.shicloud.utils.ByteUtil; 4 | 5 | /** 6 | * Created by shifeng on 2018/10/31 7 | * 8 | */ 9 | public class Test { 10 | 11 | public static void main(String[] args) throws Exception { 12 | byte[] msg = new byte[0]; 13 | msg = ByteUtil.appendBytes(msg, ByteUtil.shortToBytes(Short.valueOf("1001")));// offset 14 | msg = ByteUtil.appendBytes(msg, ByteUtil.intToBytes(54321)); 15 | msg = ByteUtil.appendBytes(msg, ByteUtil.floatToBytes(123.45f)); 16 | msg = ByteUtil.appendBytes(msg, ByteUtil.intToBytes(1234)); 17 | ByteUtil.printBytes(msg); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/config/BridgeConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.config; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @ConfigurationProperties(prefix = "bridge", ignoreUnknownFields = false) 10 | public class BridgeConfig{ 11 | 12 | String modelPath; 13 | List mqtts; 14 | 15 | public String getModelPath() { 16 | return modelPath; 17 | } 18 | 19 | public void setModelPath(String modelPath) { 20 | this.modelPath = modelPath; 21 | } 22 | 23 | public List getMqtts() { 24 | return mqtts; 25 | } 26 | 27 | public void setMqtts(List mqtts) { 28 | this.mqtts = mqtts; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /release/config/model.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "user", 4 | "topic": "user_topic", 5 | "clientId": "user_client", 6 | "cleanSession": true, 7 | "qos": 1, 8 | "storeType": "mysql", 9 | "fields": [{ 10 | "name": "id", 11 | "type": "long", 12 | "index": 1, 13 | "lenght": 8, 14 | "offset": 2, 15 | "idType": "auto" 16 | }, 17 | { 18 | "name": "age", 19 | "type": "short", 20 | "index": 2, 21 | "lenght": 2 22 | }, 23 | { 24 | "name": "weight", 25 | "type": "float", 26 | "index": 3, 27 | "lenght": 4 28 | }, 29 | { 30 | "name": "salary", 31 | "type": "double", 32 | "index": 4, 33 | "lenght": 8 34 | }, 35 | { 36 | "name": "loginLength", 37 | "type": "int", 38 | "index": 5, 39 | "lenght": 2, 40 | "isTransient": true 41 | }, 42 | { 43 | "name": "login", 44 | "type": "byte[]", 45 | "index": 6, 46 | "dependsOn": 5 47 | }, 48 | { 49 | "name": "createTime", 50 | "type": "date", 51 | "index": 7, 52 | "lenght": 8 53 | }, 54 | { 55 | "name": "isMale", 56 | "type": "boolean", 57 | "index": 8, 58 | "lenght": 1 59 | }, 60 | { 61 | "name": "isDel", 62 | "type": "byte", 63 | "index": 9, 64 | "lenght": 1 65 | }] 66 | } 67 | ] 68 | -------------------------------------------------------------------------------- /release/config/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] - %msg%n 11 | 12 | 13 | 14 | 16 | 17 | 18 | ${LOG_HOME}/iot-mqtt-bridge.%d{yyyy-MM-dd}.log 19 | 20 | 21 | 22 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] - %msg%n 23 | 24 | 25 | 27 | 100MB 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/config/MqttConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.config; 2 | 3 | import java.util.List; 4 | 5 | public class MqttConfig{ 6 | String url; 7 | String username; 8 | String password; 9 | Short keepAlive; 10 | Boolean retained; 11 | Integer reconnectAttemptsMax; 12 | Integer reconnectDelay; 13 | List models; 14 | 15 | public String getUrl() { 16 | return url; 17 | } 18 | public void setUrl(String url) { 19 | this.url = url; 20 | } 21 | public String getUsername() { 22 | return username; 23 | } 24 | public void setUsername(String username) { 25 | this.username = username; 26 | } 27 | public String getPassword() { 28 | return password; 29 | } 30 | public void setPassword(String password) { 31 | this.password = password; 32 | } 33 | public Short getKeepAlive() { 34 | return keepAlive; 35 | } 36 | public void setKeepAlive(Short keepAlive) { 37 | this.keepAlive = keepAlive; 38 | } 39 | public Boolean getRetained() { 40 | return retained; 41 | } 42 | public void setRetained(Boolean retained) { 43 | this.retained = retained; 44 | } 45 | public Integer getReconnectAttemptsMax() { 46 | return reconnectAttemptsMax; 47 | } 48 | public void setReconnectAttemptsMax(Integer reconnectAttemptsMax) { 49 | this.reconnectAttemptsMax = reconnectAttemptsMax; 50 | } 51 | public Integer getReconnectDelay() { 52 | return reconnectDelay; 53 | } 54 | public void setReconnectDelay(Integer reconnectDelay) { 55 | this.reconnectDelay = reconnectDelay; 56 | } 57 | public List getModels() { 58 | return models; 59 | } 60 | public void setModels(List models) { 61 | this.models = models; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/com/github/shicloud/bridge/BridgeSendTest.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge; 2 | 3 | import org.fusesource.hawtbuf.Buffer; 4 | import org.fusesource.hawtbuf.UTF8Buffer; 5 | import org.fusesource.mqtt.client.QoS; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.github.shicloud.mqtt.client.MqttBaseHandler; 10 | import com.github.shicloud.mqtt.client.config.ClientConfig; 11 | import com.github.shicloud.utils.ByteUtil; 12 | 13 | /** 14 | * Created by shifeng on 2018/10/31 15 | * 16 | */ 17 | public class BridgeSendTest extends MqttBaseHandler { 18 | private static Logger logger = LoggerFactory.getLogger(BridgeSendTest.class); 19 | 20 | public static void main(String[] args) throws Exception { 21 | if(args.length == 0) { 22 | logger.error("please set config path"); 23 | System.exit(-1); 24 | } 25 | BridgeSendTest handler = new BridgeSendTest(); 26 | ClientConfig properties = new ClientConfig(args[0]); 27 | handler.init(properties, null,"sendTest", false); 28 | 29 | logger.info("sendTest inited"); 30 | 31 | //for (int i = 0; i < 10000; i++) { 32 | byte[] msg = new byte[0]; 33 | msg = ByteUtil.appendBytes(msg, ByteUtil.shortToBytes(Short.valueOf("1001")));// offset 34 | msg = ByteUtil.appendBytes(msg, ByteUtil.intToBytes(54321)); 35 | msg = ByteUtil.appendBytes(msg, ByteUtil.floatToBytes(123.45f)); 36 | msg = ByteUtil.appendBytes(msg, ByteUtil.intToBytes(1234)); 37 | 38 | handler.send("device_info_topic", msg, QoS.values()[1], false); 39 | Thread.sleep(100); 40 | //} 41 | Thread.sleep(10000); 42 | } 43 | 44 | @Override 45 | public void processInput(UTF8Buffer topic, Buffer payload) { 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/model/Model.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.model; 2 | 3 | import java.util.List; 4 | 5 | public class Model { 6 | 7 | private String name; 8 | private String topic; 9 | private String clientId; 10 | private Integer qos; 11 | private Boolean cleanSession; 12 | private String storeType; 13 | private String tableName; 14 | private List fields; 15 | 16 | private Class clazz; 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | public void setName(String name) { 22 | this.name = name; 23 | } 24 | public List getFields() { 25 | return fields; 26 | } 27 | public void setFields(List fields) { 28 | this.fields = fields; 29 | } 30 | public String getTopic() { 31 | return topic; 32 | } 33 | public void setTopic(String topic) { 34 | this.topic = topic; 35 | } 36 | 37 | public String getClientId() { 38 | return clientId; 39 | } 40 | public void setClientId(String clientId) { 41 | this.clientId = clientId; 42 | } 43 | public Integer getQos() { 44 | return qos; 45 | } 46 | public void setQos(Integer qos) { 47 | this.qos = qos; 48 | } 49 | public Boolean getCleanSession() { 50 | return cleanSession; 51 | } 52 | public void setCleanSession(Boolean cleanSession) { 53 | this.cleanSession = cleanSession; 54 | } 55 | public String getStoreType() { 56 | return storeType; 57 | } 58 | public void setStoreType(String storeType) { 59 | this.storeType = storeType; 60 | } 61 | public String getTableName() { 62 | return tableName; 63 | } 64 | public void setTableName(String tableName) { 65 | this.tableName = tableName; 66 | } 67 | public Class getClazz() { 68 | return clazz; 69 | } 70 | public void setClazz(Class clazz) { 71 | this.clazz = clazz; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/model/Field.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.model; 2 | 3 | public class Field { 4 | 5 | private String name; 6 | 7 | private String type; 8 | 9 | private Integer divide; 10 | 11 | private Integer index; 12 | 13 | private Integer lenght; 14 | 15 | private Integer offset; 16 | 17 | private Integer dependsOn; 18 | 19 | private Boolean isTransient; 20 | 21 | private String idType; 22 | 23 | private Boolean isLittleEnd; 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | public void setName(String name) { 29 | this.name = name; 30 | } 31 | public String getType() { 32 | return type; 33 | } 34 | public void setType(String type) { 35 | this.type = type; 36 | } 37 | public Integer getDivide() { 38 | return divide; 39 | } 40 | public void setDivide(Integer divide) { 41 | this.divide = divide; 42 | } 43 | public Integer getIndex() { 44 | return index; 45 | } 46 | public void setIndex(Integer index) { 47 | this.index = index; 48 | } 49 | public Integer getLenght() { 50 | return lenght; 51 | } 52 | public void setLenght(Integer lenght) { 53 | this.lenght = lenght; 54 | } 55 | public Integer getOffset() { 56 | return offset; 57 | } 58 | public void setOffset(Integer offset) { 59 | this.offset = offset; 60 | } 61 | public Integer getDependsOn() { 62 | return dependsOn; 63 | } 64 | public void setDependsOn(Integer dependsOn) { 65 | this.dependsOn = dependsOn; 66 | } 67 | public Boolean getIsTransient() { 68 | return isTransient; 69 | } 70 | public void setIsTransient(Boolean isTransient) { 71 | this.isTransient = isTransient; 72 | } 73 | public String getIdType() { 74 | return idType; 75 | } 76 | public void setIdType(String idType) { 77 | this.idType = idType; 78 | } 79 | public Boolean getIsLittleEnd() { 80 | return isLittleEnd; 81 | } 82 | public void setIsLittleEnd(Boolean isLittleEnd) { 83 | this.isLittleEnd = isLittleEnd; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/Startup.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge; 2 | 3 | import java.io.File; 4 | import java.util.Map; 5 | 6 | import org.apache.commons.io.FileUtils; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.ApplicationArguments; 11 | import org.springframework.boot.ApplicationRunner; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | import org.springframework.boot.builder.SpringApplicationBuilder; 14 | import org.springframework.scheduling.annotation.EnableAsync; 15 | import org.springframework.transaction.annotation.EnableTransactionManagement; 16 | 17 | import com.github.shicloud.bridge.config.BridgeConfig; 18 | import com.github.shicloud.bridge.config.MqttConfig; 19 | import com.github.shicloud.bridge.model.Model; 20 | import com.github.shicloud.bridge.model.ModelParser; 21 | import com.github.shicloud.bridge.mysql.MsgHandler; 22 | import com.github.shicloud.jdbc.templete.JdbcTemplateTool; 23 | 24 | @SpringBootApplication(scanBasePackages = "com.github.shicloud") 25 | @EnableAsync 26 | @EnableTransactionManagement(proxyTargetClass = true) 27 | public class Startup implements ApplicationRunner { 28 | private static final Logger log = LoggerFactory.getLogger(Startup.class); 29 | 30 | @Autowired 31 | BridgeConfig bridgeConfig; 32 | @Autowired 33 | JdbcTemplateTool jtt; 34 | 35 | public static void main(String[] args) { 36 | new SpringApplicationBuilder().sources(Startup.class).run(args); 37 | } 38 | 39 | @Override 40 | public void run(ApplicationArguments args) throws Exception { 41 | String json = FileUtils.readFileToString(new File(bridgeConfig.getModelPath())); 42 | Map modelMap = ModelParser.loadAll(json); 43 | 44 | for (MqttConfig mqttConfig : bridgeConfig.getMqtts()) { 45 | for (String modelName : mqttConfig.getModels()) { 46 | new MsgHandler(modelMap.get(modelName), mqttConfig, jtt); 47 | } 48 | } 49 | 50 | log.info("startup success."); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/com/github/shicloud/bridge/BridgeSendComplexTest.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge; 2 | 3 | import java.util.Date; 4 | 5 | import org.fusesource.hawtbuf.Buffer; 6 | import org.fusesource.hawtbuf.UTF8Buffer; 7 | import org.fusesource.mqtt.client.QoS; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import com.github.shicloud.mqtt.client.MqttBaseHandler; 12 | import com.github.shicloud.mqtt.client.config.ClientConfig; 13 | import com.github.shicloud.utils.ByteUtil; 14 | 15 | /** 16 | * Created by shifeng on 2018/10/31 17 | * 18 | */ 19 | public class BridgeSendComplexTest extends MqttBaseHandler { 20 | private static Logger logger = LoggerFactory.getLogger(BridgeSendComplexTest.class); 21 | 22 | public static void main(String[] args) throws Exception { 23 | if(args.length == 0) { 24 | logger.error("please set config path"); 25 | System.exit(-1); 26 | } 27 | BridgeSendComplexTest handler = new BridgeSendComplexTest(); 28 | ClientConfig properties = new ClientConfig(args[0]); 29 | handler.init(properties, null,"sendComplexTest", false); 30 | 31 | logger.info("sendComplexTest inited"); 32 | 33 | 34 | byte[] msg = new byte[0]; 35 | msg = ByteUtil.appendBytes(msg, ByteUtil.shortToBytes(Short.valueOf("1001")));// deviceId 36 | byte[] b = new byte[0]; 37 | for (int i = 0; i < 5; i++) { 38 | b = ByteUtil.appendBytes(b, ByteUtil.shortToBytes(Short.valueOf(String.valueOf(5+i)))); 39 | b = ByteUtil.appendBytes(b, ("hello").getBytes()); 40 | for (int j = 0; j < i; j++) { 41 | b = ByteUtil.appendBytes(b, "A".getBytes()); 42 | } 43 | b = ByteUtil.appendBytes(b, ByteUtil.longToBytes(new Date().getTime())); 44 | } 45 | msg = ByteUtil.appendBytes(msg, ByteUtil.intToBytes(b.length)); 46 | msg = ByteUtil.appendBytes(msg, b); 47 | msg = ByteUtil.appendBytes(msg, ByteUtil.intToBytes(1234)); 48 | handler.send("device_info_topic", msg, QoS.values()[1], false); 49 | 50 | Thread.sleep(10000); 51 | } 52 | 53 | @Override 54 | public void processInput(UTF8Buffer topic, Buffer payload) { 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # iot-mqtt-bridge 2 | **feature:** 3 | 4 | - [x] parser basic type data into mysql 5 | - [ ] parser basic type data into kafka 6 | - [ ] support more complex byte data persistence 7 | - [ ] batch save data 8 | - [ ] custom mqtt handler 9 | 10 | you just define a description json file in model.json, 11 | 12 | and mqtt connect info and mysql db info in bridge.yml 13 | 14 | then run release/bin/server to start 15 | 16 | this tools will automate save mqtt byte data into db. 17 | 18 | ```json 19 | [ 20 | { 21 | "name": "user", //"this will become class name" 22 | "topic": "user_topic", 23 | "clientId": "user_client", 24 | "cleanSession": true, 25 | "qos": 1, 26 | "storeType": "mysql", 27 | "fields": [{ 28 | "name": "id", 29 | "type": "long", //"this will become field type" 30 | "index": 1, 31 | "lenght": 8, //"how many bytes will be cut into this field" 32 | "offset": 2, 33 | "idType": "auto" //"this field will use db auto gene value, insert into db" 34 | }, 35 | { 36 | "name": "loginLength", 37 | "type": "int", 38 | "index": 2, 39 | "lenght": 2, 40 | "isTransient": true //"this will not saved into db" 41 | }, 42 | { 43 | "name": "login", 44 | "type": "byte[]", 45 | "index": 3, 46 | "dependsOn": 2 //"according loginLength value to cut bytes" 47 | } 48 | 49 | ] 50 | ``` 51 | 52 | ```yaml 53 | spring: 54 | datasource: 55 | driver-class-name: com.mysql.jdbc.Driver 56 | url: jdbc:mysql://localhost:3306/mqtt_test 57 | username: 58 | password: 59 | jdbc: 60 | template: 61 | prefix: t_ 62 | suffix: _test 63 | insertGetId: false 64 | logging: 65 | config: logback.xml 66 | bridge: 67 | modelPath: model.json 68 | mqtts: 69 | - url: tcp://localhost:1883 70 | username: 71 | password: 72 | keepAlive: 20 73 | retained: false 74 | reconnectAttemptsMax: -1 75 | reconnectDelay: 10 76 | models: 77 | - user 78 | 79 | 80 | ``` 81 | 82 | ![欢迎大家一起交流](https://github.com/ShiCloud/iot-mqtt/blob/master/pic/iot-mqtt%E7%BE%A4%E8%81%8A%E4%BA%8C%E7%BB%B4%E7%A0%81.png) 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iot-mqtt-bridge 2 | **feature:** 3 | 4 | - [x] 解析简单mqtt消息存到mysql 5 | - [x] 解析复杂消息 6 | - [ ] 解析简单mqtt消息存到kafka 7 | - [ ] 批处理存储数据 8 | - [ ] 自定义各种handler 9 | 10 | 11 | 12 | 假如有一个这种结构的消息,要存储到t_device_info表里 13 | 14 | | 列名 | device_id | msg_code | msg_value | serial num | 15 | | -------- | --------- | ---------- | -------------- | ---------- | 16 | | 数据类型 | short | int | float | int | 17 | | 原值 | 3,-23 | 0,0,-44,49 | 66,-10,-26,102 | 0,0,4,-46 | 18 | | 实际值 | 1001 | 10000 | 123.45 | 4321 | 19 | 20 | 可以如下配置: 21 | 22 | ```sql 23 | DROP TABLE IF EXISTS t_device_info; 24 | CREATE TABLE t_device_info ( 25 | id int AUTO_INCREMENT, 26 | device_id smallint NOT NULL, 27 | msg_code int, 28 | msg_value int, 29 | time_stamp datetime, 30 | PRIMARY KEY (id) 31 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 32 | ``` 33 | 34 | 35 | 36 | ```json 37 | [ 38 | { 39 | "name": "deviceInfo", 40 | "tableName": "t_device_info", 41 | "topic": "device_info_topic", 42 | "clientId": "device_info_client", 43 | "cleanSession": true, 44 | "qos": 1, 45 | "storeType": "mysql", 46 | "fields": [{ 47 | "name": "deviceId", 48 | "type": "short", 49 | "index": 1, 50 | "lenght": 2 51 | }, 52 | { 53 | "name": "msgCode", 54 | "type": "int", 55 | "index": 2, 56 | "lenght": 4 57 | }, 58 | { 59 | "name": "msgValue", 60 | "type": "float", 61 | "index": 3, 62 | "lenght": 4 63 | }, 64 | { 65 | "name": "serialNum", 66 | "type": "int", 67 | "index": 4, 68 | "lenght": 4 69 | }] 70 | } 71 | ] 72 | ``` 73 | 74 | 75 | 76 | ```yaml 77 | spring: 78 | datasource: 79 | driver-class-name: com.mysql.jdbc.Driver 80 | url: jdbc:mysql://localhost:3306/mqtt_test 81 | username: 82 | password: 83 | logging: 84 | config: logback.xml 85 | bridge: 86 | modelPath: model.json 87 | mqtts: 88 | - url: tcp://localhost:1883 89 | username: 90 | password: 91 | keepAlive: 20 92 | retained: false 93 | reconnectAttemptsMax: -1 94 | reconnectDelay: 10 95 | models: 96 | - deviceInfo 97 | 98 | 99 | ``` 100 | 101 | cd release/bin 102 | 103 | ./server 启动项目 104 | 105 | 发送消息后,最终数据就会被自动保存到数据库内 106 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.github.shicloud 7 | iot-mqtt-bridge 8 | 1.0 9 | jar 10 | 11 | iot-mqtt-bridge 12 | http://maven.apache.org 13 | 14 | 15 | 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | 1.5.17.RELEASE 24 | 25 | 26 | 27 | 28 | org.apache.commons 29 | commons-io 30 | 1.3.2 31 | 32 | 33 | org.javassist 34 | javassist 35 | 36 | 37 | com.alibaba 38 | fastjson 39 | 1.2.59 40 | 41 | 42 | com.github.shicloud 43 | simple-jdbc-templete 44 | 1.0.2 45 | 46 | 47 | com.github.shicloud 48 | iot-mqtt-client 49 | 1.0 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-web 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-compiler-plugin 66 | 67 | ${java.version} 68 | ${java.version} 69 | UTF-8 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | true 77 | com.github.shicloud.bridge.Startup 78 | ZIP 79 | 80 | 81 | 82 | 83 | repackage 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/mysql/MsgHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.mysql; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.concurrent.Callable; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.LinkedBlockingQueue; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import javax.persistence.Table; 15 | 16 | import org.fusesource.hawtbuf.Buffer; 17 | import org.fusesource.hawtbuf.UTF8Buffer; 18 | import org.fusesource.mqtt.client.QoS; 19 | import org.fusesource.mqtt.client.Topic; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.beans.BeanUtils; 23 | 24 | import com.github.shicloud.bridge.annotation.RefValue; 25 | import com.github.shicloud.bridge.annotation.StaticValue; 26 | import com.github.shicloud.bridge.config.MqttConfig; 27 | import com.github.shicloud.bridge.model.Model; 28 | import com.github.shicloud.bytes.ByteParser; 29 | import com.github.shicloud.jdbc.templete.JdbcTemplateTool; 30 | import com.github.shicloud.mqtt.client.MqttBaseHandler; 31 | import com.github.shicloud.mqtt.client.config.ClientConfig; 32 | import com.github.shicloud.utils.ByteUtil; 33 | import com.github.shicloud.utils.ReflectUtil; 34 | 35 | /** 36 | * Created by shifeng on 2018/10/31 37 | * 38 | */ 39 | public class MsgHandler extends MqttBaseHandler { 40 | 41 | private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors(); 42 | 43 | private static final Logger log = LoggerFactory.getLogger(MsgHandler.class); 44 | 45 | private static ThreadPoolExecutor executor = new ThreadPoolExecutor( 46 | AVAILABLE_PROCESSORS, AVAILABLE_PROCESSORS*2, 60, TimeUnit.SECONDS, 47 | new LinkedBlockingQueue<>(AVAILABLE_PROCESSORS*50), Executors.defaultThreadFactory()); 48 | 49 | private Model model; 50 | private JdbcTemplateTool jtt; 51 | 52 | public MsgHandler(Model model, MqttConfig mqttConfig, JdbcTemplateTool jtt) { 53 | this.model = model; 54 | this.jtt = jtt; 55 | ClientConfig properties = new ClientConfig(); 56 | BeanUtils.copyProperties(mqttConfig, properties); 57 | 58 | Topic[] topics = new Topic[] { new Topic(model.getTopic(), QoS.values()[model.getQos()]) }; 59 | this.init(properties, topics, model.getClientId(), model.getCleanSession()); 60 | log.info("model {} mqtt client inited", model.getName()); 61 | } 62 | 63 | @Override 64 | public void processInput(UTF8Buffer topic, Buffer payload) { 65 | String topicStr = ByteUtil.byteToStr(ByteUtil.hexStrToBytes(topic.hex())); 66 | byte[] msg = ByteUtil.hexStrToBytes(payload.hex()); 67 | ByteUtil.printBytes(msg); 68 | Object object = ByteParser.toObject(msg, model.getClazz()); 69 | if (object == null) { 70 | return; 71 | } 72 | long start = System.currentTimeMillis(); 73 | boolean rs = false; 74 | try { 75 | rs = executor.submit(new Callable() { 76 | @Override 77 | public Boolean call() throws Exception { 78 | processObject(object); 79 | return true; 80 | } 81 | }).get(10000, TimeUnit.MILLISECONDS); 82 | } catch (Exception e) { 83 | log.warn("{} processInput error : ", topicStr); 84 | } 85 | if (!rs) { 86 | log.warn("{} processInput is interrupted topic name : ", topicStr); 87 | } 88 | long cost = System.currentTimeMillis() - start; 89 | log.debug("{} processInput cost time:{}", topicStr, cost); 90 | 91 | } 92 | 93 | private void processObject(Object object) { 94 | Map staticValues = new HashMap<>(); 95 | List list = null; 96 | Field[] fields = object.getClass().getDeclaredFields(); 97 | for (int i = 0; i < fields.length; i++) { 98 | Field field = fields[i]; 99 | try { 100 | StaticValue staticValue = field.getAnnotation(StaticValue.class); 101 | if (staticValue != null) { 102 | Method getter = ReflectUtil.getGetter(object, field.getName()); 103 | staticValues.put(staticValue.name(), getter.invoke(object)); 104 | } 105 | if (field.getType() == List.class) { 106 | Method getter = ReflectUtil.getGetter(object, field.getName()); 107 | list = (List) getter.invoke(object); 108 | } 109 | } catch (Exception e) { 110 | log.info(field.getName() + " get value failure ", e); 111 | } 112 | } 113 | 114 | save(object); 115 | 116 | if (list != null) { 117 | for (Object subObj : list) { 118 | Field[] subFields = subObj.getClass().getDeclaredFields(); 119 | for (int i = 0; i < subFields.length; i++) { 120 | Field subField = subFields[i]; 121 | try { 122 | RefValue refValue = subField.getAnnotation(RefValue.class); 123 | if (refValue != null) { 124 | Method setter = ReflectUtil.getSetter(subObj, subField.getName()); 125 | setter.invoke(subObj, staticValues.get(refValue.target())); 126 | } 127 | } catch (Exception e) { 128 | log.info(subField.getName() + " set value failure ", e); 129 | } 130 | } 131 | save(subObj); 132 | } 133 | } 134 | } 135 | 136 | private void save(Object obj) { 137 | if (obj.getClass().getAnnotation(Table.class) != null) { 138 | try { 139 | jtt.insert(obj); 140 | } catch (Exception e) { 141 | log.info("{} insert mysql db error ", obj.getClass().getName(), e); 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/github/shicloud/bridge/model/ModelParser.java: -------------------------------------------------------------------------------- 1 | package com.github.shicloud.bridge.model; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Modifier; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.ExecutionException; 10 | import java.util.concurrent.ForkJoinPool; 11 | import java.util.concurrent.ForkJoinTask; 12 | 13 | import org.apache.commons.io.FileUtils; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.beans.BeanUtils; 17 | import org.springframework.util.ResourceUtils; 18 | import org.springframework.util.StringUtils; 19 | 20 | import com.alibaba.fastjson.JSON; 21 | import com.github.shicloud.jdbc.annotation.ID; 22 | import com.github.shicloud.utils.CamelNameUtils; 23 | 24 | import javassist.CannotCompileException; 25 | import javassist.ClassPool; 26 | import javassist.CtClass; 27 | import javassist.CtField; 28 | import javassist.CtNewMethod; 29 | import javassist.NotFoundException; 30 | import javassist.bytecode.AnnotationsAttribute; 31 | import javassist.bytecode.ClassFile; 32 | import javassist.bytecode.ConstPool; 33 | import javassist.bytecode.annotation.Annotation; 34 | import javassist.bytecode.annotation.EnumMemberValue; 35 | import javassist.bytecode.annotation.IntegerMemberValue; 36 | import javassist.bytecode.annotation.StringMemberValue; 37 | 38 | public class ModelParser { 39 | private static final String REF_VALUE_PACKAGE = "com.github.shicloud.bridge.annotation.RefValue"; 40 | private static final String STATIC_VALUE_PACKAGE = "com.github.shicloud.bridge.annotation.StaticValue"; 41 | private static final String MODEL_PACKAGE = "com.github.shicloud.bridge.pojo."; 42 | private static final String TABLE_PACKAGE = "javax.persistence.Table"; 43 | private static final String TRANSIENT_PACKAGE = "javax.persistence.Transient"; 44 | private static final String ANNOTATION_ID_PACKAGE = "com.github.shicloud.jdbc.annotation.ID"; 45 | private static final String PARSER_PACKAGE = "com.github.shicloud.bytes.annotation.Parser"; 46 | private static final String TARGET_MODEL_PACKAGE = "com.github.shicloud.bytes.annotation.TargetModel"; 47 | private static final String LITTLE_END_PACKAGE = "com.github.shicloud.bytes.annotation.LittleEnd"; 48 | 49 | private static final Logger log = LoggerFactory.getLogger(ModelParser.class); 50 | 51 | public static ClassPool POOL = ClassPool.getDefault(); 52 | 53 | private static Map modelMap = new HashMap<>(); 54 | private static Map staticFieldMap = new HashMap<>(); 55 | 56 | public static Map getModelMap() { 57 | return modelMap; 58 | } 59 | 60 | public static Map getStaticFieldMap() { 61 | return staticFieldMap; 62 | } 63 | 64 | public static Map loadAll(String json) { 65 | List models = JSON.parseArray(json, Model.class); 66 | for (Model model : models) { 67 | String name = model.getName(); 68 | if (StringUtils.isEmpty(name)) { 69 | log.error("model name is empty, please set it "); 70 | System.exit(-1); 71 | } 72 | modelMap.put(model.getName(), model); 73 | } 74 | ForkJoinPool forkJoinPool = new ForkJoinPool(); 75 | List> tasks = new ArrayList<>(); 76 | for (Model model : modelMap.values()) { 77 | ForkJoinTask task = forkJoinPool.submit(new Runnable() { 78 | @Override 79 | public void run() { 80 | try { 81 | createClass(model); 82 | } catch (Exception e) { 83 | log.error("create class "+model.getName()+" failure ",e); 84 | System.exit(-1); 85 | } 86 | } 87 | }); 88 | tasks.add(task); 89 | } 90 | try { 91 | for (ForkJoinTask forkJoinTask : tasks) { 92 | forkJoinTask.get(); 93 | } 94 | } catch (InterruptedException | ExecutionException e) { 95 | log.error("loadAll modle failure ",e); 96 | System.exit(-1); 97 | } 98 | return modelMap; 99 | } 100 | 101 | 102 | private static void createClass(Model model) throws Exception { 103 | String name = model.getName(); 104 | String upperName = upperFirstStr(name); 105 | String className = MODEL_PACKAGE + upperName; 106 | CtClass ctClass = POOL.makeClass(className); 107 | ClassFile ccFile = ctClass.getClassFile(); 108 | ConstPool constPool = ccFile.getConstPool(); 109 | 110 | if (model.getTableName() != null) { 111 | AnnotationsAttribute attr = new AnnotationsAttribute(constPool, 112 | AnnotationsAttribute.visibleTag); 113 | Annotation table = new Annotation(TABLE_PACKAGE, constPool); 114 | table.addMemberValue("name", new StringMemberValue(model.getTableName(), constPool)); 115 | attr.addAnnotation(table); 116 | ccFile.addAttribute(attr); 117 | } 118 | 119 | for (Field field : model.getFields()) { 120 | try { 121 | createField(ctClass, field); 122 | } catch (CannotCompileException | NotFoundException e) { 123 | log.error(upperName + " class create field {} error", field.getName(), e); 124 | } 125 | } 126 | 127 | String classpath = ResourceUtils.getURL("classpath:").getPath(); 128 | log.debug("classpath::" + classpath); 129 | FileUtils.writeByteArrayToFile(new File(classpath + className.replace(".", "/") + ".class"), 130 | ctClass.toBytecode()); 131 | Class c = Class.forName(className); 132 | 133 | model.setClazz(c); 134 | } 135 | 136 | private static void createField(CtClass ctClass, Field field) 137 | throws CannotCompileException, NotFoundException, InterruptedException, ClassNotFoundException { 138 | String name = field.getName(); 139 | if (StringUtils.isEmpty(name)) { 140 | log.error("field name is empty, please set it "); 141 | System.exit(-1); 142 | } 143 | 144 | ClassFile ccFile = ctClass.getClassFile(); 145 | ConstPool constPool = ccFile.getConstPool(); 146 | Annotation staticValueAnno = null; 147 | if (name.startsWith("@")) { 148 | name = name.replace("@", ""); 149 | int lastIndexOf = ctClass.getName().lastIndexOf("."); 150 | String staticName = ctClass.getName().substring(lastIndexOf+1)+"."+name; 151 | staticFieldMap.put(staticName, field); 152 | staticValueAnno = new Annotation(STATIC_VALUE_PACKAGE, constPool); 153 | staticValueAnno.addMemberValue("name",new StringMemberValue(staticName, constPool)); 154 | } 155 | Annotation refValueAnno = null; 156 | if (name.startsWith("&")) { 157 | String refName = upperFirstStr(name.replace("&", "")); 158 | while(staticFieldMap.get(refName)==null) { 159 | Thread.sleep(100); 160 | log.debug("get ref field "+refName+" is null, sleep 100ms"); 161 | } 162 | Field refField = staticFieldMap.get(refName); 163 | int index = field.getIndex()!=null?field.getIndex():0; 164 | int offset = field.getOffset()!=null?field.getOffset():0; 165 | int lastIndexOf = refName.lastIndexOf("."); 166 | 167 | name = refName.substring(lastIndexOf+1); 168 | 169 | BeanUtils.copyProperties(refField, field); 170 | field.setName(name); 171 | field.setIndex(index); 172 | field.setLenght(0); 173 | field.setOffset(offset); 174 | field.setDependsOn(0); 175 | 176 | refValueAnno = new Annotation(REF_VALUE_PACKAGE, constPool); 177 | refValueAnno.addMemberValue("target",new StringMemberValue(refName, constPool)); 178 | } 179 | 180 | 181 | CtField f = new CtField(getType(field.getType()), name, ctClass); 182 | f.setModifiers(Modifier.PRIVATE); 183 | ctClass.addField(f); 184 | String upperFieldStr = upperFirstStr(name); 185 | ctClass.addMethod(CtNewMethod.getter("get" + upperFieldStr, f)); 186 | ctClass.addMethod(CtNewMethod.setter("set" + upperFieldStr, f)); 187 | 188 | AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); 189 | Annotation parser = new Annotation(PARSER_PACKAGE, constPool); 190 | parser.addMemberValue("index", 191 | new IntegerMemberValue(constPool, field.getIndex() != null ? field.getIndex() : 0)); 192 | parser.addMemberValue("lenght", 193 | new IntegerMemberValue(constPool, field.getLenght() != null ? field.getLenght() : 0)); 194 | parser.addMemberValue("offset", 195 | new IntegerMemberValue(constPool, field.getOffset() != null ? field.getOffset() : 0)); 196 | parser.addMemberValue("dependsOn", 197 | new IntegerMemberValue(constPool, field.getDependsOn() != null ? field.getDependsOn() : 0)); 198 | parser.addMemberValue("divide", 199 | new IntegerMemberValue(constPool, field.getDivide() != null ? field.getDivide() : 1)); 200 | attr.addAnnotation(parser); 201 | 202 | 203 | if (field.getIsTransient() != null && field.getIsTransient()) { 204 | Annotation transientAnno = new Annotation(TRANSIENT_PACKAGE, constPool); 205 | attr.addAnnotation(transientAnno); 206 | } 207 | if (field.getIsLittleEnd() != null && field.getIsLittleEnd()) { 208 | Annotation LittleEndAnno = new Annotation(LITTLE_END_PACKAGE, constPool); 209 | attr.addAnnotation(LittleEndAnno); 210 | } 211 | if (field.getIdType() != null) { 212 | Annotation idAnno = new Annotation(ANNOTATION_ID_PACKAGE, constPool); 213 | EnumMemberValue enumValue = new EnumMemberValue(constPool); 214 | enumValue.setType(ID.TYPE.class.getName()); 215 | enumValue.setValue( 216 | "AUTO".equals(field.getIdType().toUpperCase()) ? ID.TYPE.AUTO.name() : ID.TYPE.INPUT.name()); 217 | idAnno.addMemberValue("value", enumValue); 218 | idAnno.addMemberValue("column", 219 | new StringMemberValue(CamelNameUtils.camel2underscore(field.getName()), constPool)); 220 | attr.addAnnotation(idAnno); 221 | } 222 | if (field.getType() != null && field.getType().startsWith("list&")) { 223 | Annotation targetModelAnno = new Annotation(TARGET_MODEL_PACKAGE, constPool); 224 | int lastIndexOf = field.getType().lastIndexOf("&"); 225 | targetModelAnno.addMemberValue("value", 226 | new StringMemberValue(MODEL_PACKAGE+upperFirstStr(field.getType().substring(lastIndexOf+1)), constPool)); 227 | attr.addAnnotation(targetModelAnno); 228 | } 229 | if (refValueAnno != null) { 230 | attr.addAnnotation(refValueAnno); 231 | } 232 | if (staticValueAnno != null) { 233 | attr.addAnnotation(staticValueAnno); 234 | } 235 | f.getFieldInfo().addAttribute(attr); 236 | 237 | 238 | } 239 | 240 | private static CtClass getType(String type) throws NotFoundException { 241 | String t = type.toLowerCase(); 242 | if ("int".equals(t)) { 243 | t = "java.lang.Integer"; 244 | } else if ("date".equals(t)) { 245 | t = "java.util.Date"; 246 | } else if ("byte[]".equals(t)) { 247 | t = byte[].class.getTypeName(); 248 | } else if (t != null && t.startsWith("list&")) { 249 | t = "java.util.List"; 250 | } else { 251 | t = "java.lang." + upperFirstStr(t); 252 | } 253 | 254 | return POOL.getCtClass(t); 255 | } 256 | 257 | private static String upperFirstStr(String str) { 258 | String namePart1 = str.substring(0, 1).toUpperCase(); 259 | String namePart2 = str.substring(1); 260 | StringBuffer upperName = new StringBuffer().append(namePart1 + namePart2); 261 | return upperName.toString(); 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /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, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------