commit c347ceaf3a309ac084ef496644eaaefa8da11262 Author: Jaka Ramdani Date: Thu May 28 21:17:51 2026 +0700 initial import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3bad3d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +logs/ diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..323174a --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project utms-agent. + + + diff --git a/conf/mqtt-agents-log4j.cfg b/conf/mqtt-agents-log4j.cfg new file mode 100644 index 0000000..398dc4d --- /dev/null +++ b/conf/mqtt-agents-log4j.cfg @@ -0,0 +1,16 @@ +# +# our log4j properties / configuration FILE +# +# Direct log messages to a rolling log file. +log4j.appender.FILE=org.apache.log4j.RollingFileAppender +log4j.appender.FILE.File=logs/utms-agent.log +log4j.appender.FILE.MaxFileSize=100MB +log4j.appender.FILE.MaxBackupIndex=10 +log4j.appender.FILE.Append=true +log4j.appender.FILE.layout=org.apache.log4j.PatternLayout +log4j.appender.FILE.layout.ConversionPattern=%d [%-5p] [%t] [%c{1}] - %m%n + +# Log only to FILE. +log4j.rootLogger=WARN, FILE +log4j.logger.id.iptek.utms.agent=INFO, FILE +log4j.additivity.id.iptek.utms.agent=false diff --git a/conf/mqtt-agents.cfg b/conf/mqtt-agents.cfg new file mode 100644 index 0000000..8fddad9 --- /dev/null +++ b/conf/mqtt-agents.cfg @@ -0,0 +1,114 @@ +## Logger +log4j.file = ${Config.Dir}/mqtt-agents-log4j.cfg + +## Database +### if 'db.datasource' has been set or not empty, ignore the rest +db.driver = org.postgresql.Driver +db.url = jdbc:postgresql://localhost:5432/utms_new +db.user = utms +db.password = utms1234 + +# Mqtt Configuration +#mqtt.broker.url = tcp://unifiedtms.id:1883 +mqtt.broker.url = tcp://192.168.4.112:1883 +mqtt.clientid_prefix = SENDER_ +mqtt.user = user1 +mqtt.password = P@ssw0rd +mqtt.keepalive = 10 +mqtt.maxinflight = 32 +mqtt.cleansession = true +mqtt.autoreconnect = true +## Pending Task Configuration +pendingtask.query.limit = 100 +pendingtask.periode.minutes = 30 + +# Minio +fileserver.endpoint = https://download.unifiedtms.id:9000 +fileserver.access_key = i9ZB0sNhHaiQe7oEP0sL +fileserver.secret_key = 4bxVnKwKgkXpZJ9Dou4oprZ1E8oe3s1HuvKLRn0N +fileserver.bucket = application-bucket +fileserver.icon.path = icons +fileserver.icon.expiry = 7 +fileserver.icon.timeunit = DAYS +fileserver.app.path = apps +fileserver.app.expiry = 7 +fileserver.app.timeunit = DAYS + +## Consumers +mqtt.consumers = heartbeat_c, diagnostic_c, device_init_c, download_ack_c +#, diagnostic_c, device_init_c, download_ack_c +### heartbeat_c +mqtt.consumer.heartbeat_c.loggingid = $queue/HEARTBEAT +#mqtt.consumer.heartbeat_c.topic = $share/def/SERVER_IN_HB_2 +mqtt.consumer.heartbeat_c.topic = SERVER_IN_HB_2 +mqtt.consumer.heartbeat_c.threads = 3 +mqtt.consumer.heartbeat_c.clientid_prefix = SNHB_ +mqtt.consumer.heartbeat_c.user = user1 +mqtt.consumer.heartbeat_c.password = P@ssw0rd +mqtt.consumer.heartbeat_c.sleep = 300000 +mqtt.consumer.heartbeat_c.keepalive = 10 +mqtt.consumer.heartbeat_c.maxinflight = 32 +mqtt.consumer.heartbeat_c.cleansession = true +mqtt.consumer.heartbeat_c.autoreconnect = true +mqtt.consumer.heartbeat_c.workerclass = id.iptek.utms.agent.worker.HeartbeatWorker +mqtt.consumer.heartbeat_c.consumermode = BATCH +mqtt.consumer.heartbeat_c.batch.capacity = 100 +### diagnostic_c +mqtt.consumer.diagnostic_c.loggingid = DIAGNOSTIC +mqtt.consumer.diagnostic_c.topic = $share/def/SERVER_IN_DIAG_2 +mqtt.consumer.diagnostic_c.threads = 1 +mqtt.consumer.diagnostic_c.clientid_prefix = SNDIAG +mqtt.consumer.diagnostic_c.workerclass = SNDIAG_ +mqtt.consumer.diagnostic_c.user = user1 +mqtt.consumer.diagnostic_c.password = P@ssw0rd +mqtt.consumer.diagnostic_c.sleep = 300000 +mqtt.consumer.diagnostic_c.keepalive = 10 +mqtt.consumer.diagnostic_c.maxinflight = 32 +mqtt.consumer.diagnostic_c.cleansession = true +mqtt.consumer.diagnostic_c.autoreconnect = true +mqtt.consumer.diagnostic_c.workerclass = id.iptek.utms.agent.worker.DiagnosticWorker +mqtt.consumer.diagnostic_c.consumermode = BATCH +mqtt.consumer.diagnostic_c.batch.capacity = 10 +### device_init_c +mqtt.consumer.device_init_c.loggingid = DEVICE_INIT +mqtt.consumer.device_init_c.topic = $share/def/SERVER_IN_INIT_2 +mqtt.consumer.device_init_c.threads = 10 +mqtt.consumer.device_init_c.clientid_prefix = SNINIT +mqtt.consumer.device_init_c.workerclass = SNINIT_ +mqtt.consumer.device_init_c.user = user1 +mqtt.consumer.device_init_c.password = P@ssw0rd +mqtt.consumer.device_init_c.sleep = 300000 +mqtt.consumer.device_init_c.keepalive = 10 +mqtt.consumer.device_init_c.maxinflight = 32 +mqtt.consumer.device_init_c.cleansession = true +mqtt.consumer.device_init_c.autoreconnect = true +mqtt.consumer.device_init_c.workerclass = id.iptek.utms.agent.worker.DeviceInitWorker +mqtt.consumer.device_init_c.consumermode = SINGLE +mqtt.consumer.device_init_c.batch.capacity = 5 +### download_ack_c +mqtt.consumer.download_ack_c.loggingid = DOWNLOAD_ACK +mqtt.consumer.download_ack_c.topic = $share/def/SERVER_IN_DL_ACK +mqtt.consumer.download_ack_c.threads = 5 +mqtt.consumer.download_ack_c.clientid_prefix = SNDLACK +mqtt.consumer.download_ack_c.workerclass = SNDLACK_ +mqtt.consumer.download_ack_c.user = user1 +mqtt.consumer.download_ack_c.password = P@ssw0rd +mqtt.consumer.download_ack_c.sleep = 60000 +mqtt.consumer.download_ack_c.keepalive = 10 +mqtt.consumer.download_ack_c.maxinflight = 32 +mqtt.consumer.download_ack_c.cleansession = true +mqtt.consumer.download_ack_c.autoreconnect = true +mqtt.consumer.download_ack_c.workerclass = id.iptek.utms.agent.worker.DownloadTaskAckWorker + +## Schedulers +#schedulers = download_task_publisher +# download_task_publisher +#scheduler.download_task_publisher.jobclass = id.iptek.utms.agent.scheduler.DownloadTaskPublisher +## CRON | SIMPLE +#scheduler.download_task_publisher.trigger.type = SIMPLE +#scheduler.download_task_publisher.trigger.interval = 10 +#scheduler.download_task_publisher.trigger.repeat = 10 +#scheduler.download_task_publisher.trigger.type = CRON +#scheduler.download_task_publisher.trigger.expression = 0 0/2 8-17 * * ? + +scheduler.periode.minutes = 1 diff --git a/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar b/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar new file mode 100644 index 0000000..3683de5 Binary files /dev/null and b/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar differ diff --git a/lib/Minio-Client/minio-8.5.5-all.jar b/lib/Minio-Client/minio-8.5.5-all.jar new file mode 100644 index 0000000..4c86767 Binary files /dev/null and b/lib/Minio-Client/minio-8.5.5-all.jar differ diff --git a/lib/Minio-Client/minio-8.5.5.jar b/lib/Minio-Client/minio-8.5.5.jar new file mode 100644 index 0000000..ee6a541 Binary files /dev/null and b/lib/Minio-Client/minio-8.5.5.jar differ diff --git a/lib/Mqtt/org.eclipse.paho.client.mqttv3-1.2.5.jar b/lib/Mqtt/org.eclipse.paho.client.mqttv3-1.2.5.jar new file mode 100644 index 0000000..66f1278 Binary files /dev/null and b/lib/Mqtt/org.eclipse.paho.client.mqttv3-1.2.5.jar differ diff --git a/lib/Quartz/quartz-2.3.0-SNAPSHOT.jar b/lib/Quartz/quartz-2.3.0-SNAPSHOT.jar new file mode 100644 index 0000000..b300264 Binary files /dev/null and b/lib/Quartz/quartz-2.3.0-SNAPSHOT.jar differ diff --git a/lib/Quartz/quartz-jobs-2.3.0-SNAPSHOT.jar b/lib/Quartz/quartz-jobs-2.3.0-SNAPSHOT.jar new file mode 100644 index 0000000..1fd7a9d Binary files /dev/null and b/lib/Quartz/quartz-jobs-2.3.0-SNAPSHOT.jar differ diff --git a/lib/commons-codec-1.15.jar b/lib/commons-codec-1.15.jar new file mode 100644 index 0000000..f14985a Binary files /dev/null and b/lib/commons-codec-1.15.jar differ diff --git a/lib/commons-collections4-4.4.jar b/lib/commons-collections4-4.4.jar new file mode 100644 index 0000000..da06c3e Binary files /dev/null and b/lib/commons-collections4-4.4.jar differ diff --git a/lib/commons-compress-1.23.0.jar b/lib/commons-compress-1.23.0.jar new file mode 100644 index 0000000..38e5505 Binary files /dev/null and b/lib/commons-compress-1.23.0.jar differ diff --git a/lib/commons-id.jar b/lib/commons-id.jar new file mode 100644 index 0000000..a65e8a3 Binary files /dev/null and b/lib/commons-id.jar differ diff --git a/lib/commons-io-2.13.0.jar b/lib/commons-io-2.13.0.jar new file mode 100644 index 0000000..eb316f4 Binary files /dev/null and b/lib/commons-io-2.13.0.jar differ diff --git a/lib/commons-lang-2.0.jar b/lib/commons-lang-2.0.jar new file mode 100644 index 0000000..c8a2870 Binary files /dev/null and b/lib/commons-lang-2.0.jar differ diff --git a/lib/commons-math3-3.6.1.jar b/lib/commons-math3-3.6.1.jar new file mode 100644 index 0000000..0ff582c Binary files /dev/null and b/lib/commons-math3-3.6.1.jar differ diff --git a/lib/gson-2.8.9.jar b/lib/gson-2.8.9.jar new file mode 100644 index 0000000..3351867 Binary files /dev/null and b/lib/gson-2.8.9.jar differ diff --git a/lib/log4j-1.2.14.jar b/lib/log4j-1.2.14.jar new file mode 100644 index 0000000..6251307 Binary files /dev/null and b/lib/log4j-1.2.14.jar differ diff --git a/lib/log4j-api-2.20.0.jar b/lib/log4j-api-2.20.0.jar new file mode 100644 index 0000000..29d1b52 Binary files /dev/null and b/lib/log4j-api-2.20.0.jar differ diff --git a/lib/log4j-core-2.20.0.jar b/lib/log4j-core-2.20.0.jar new file mode 100644 index 0000000..54dafcd Binary files /dev/null and b/lib/log4j-core-2.20.0.jar differ diff --git a/lib/log4j-rolling-appender-20120206-1607-1.2.15.jar b/lib/log4j-rolling-appender-20120206-1607-1.2.15.jar new file mode 100644 index 0000000..eeaab53 Binary files /dev/null and b/lib/log4j-rolling-appender-20120206-1607-1.2.15.jar differ diff --git a/lib/nblibraries.properties b/lib/nblibraries.properties new file mode 100644 index 0000000..3170bd1 --- /dev/null +++ b/lib/nblibraries.properties @@ -0,0 +1,14 @@ +libs.CopyLibs.classpath=\ + ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar +libs.CopyLibs.displayName=CopyLibs Task +libs.CopyLibs.prop-version=3.0 +libs.Minio-Client.classpath=\ + ${base}/Minio-Client/minio-8.5.5-all.jar +libs.Minio-Client.displayName=Minio-Client +libs.Mqtt.classpath=\ + ${base}/Mqtt/org.eclipse.paho.client.mqttv3-1.2.5.jar +libs.Mqtt.displayName=Mqtt +libs.Quartz.classpath=\ + ${base}/Quartz/quartz-2.3.0-SNAPSHOT.jar;\ + ${base}/Quartz/quartz-jobs-2.3.0-SNAPSHOT.jar +libs.Quartz.displayName=Quartz diff --git a/lib/postgresql-42.2.9.jar b/lib/postgresql-42.2.9.jar new file mode 100644 index 0000000..7aba662 Binary files /dev/null and b/lib/postgresql-42.2.9.jar differ diff --git a/lib/slf4j-api-1.7.2.jar b/lib/slf4j-api-1.7.2.jar new file mode 100644 index 0000000..73f38db Binary files /dev/null and b/lib/slf4j-api-1.7.2.jar differ diff --git a/lib/slf4j-log4j12-1.7.2.jar b/lib/slf4j-log4j12-1.7.2.jar new file mode 100644 index 0000000..37a85d7 Binary files /dev/null and b/lib/slf4j-log4j12-1.7.2.jar differ diff --git a/lib/xmlbeans-5.1.1.jar b/lib/xmlbeans-5.1.1.jar new file mode 100644 index 0000000..b828ad3 Binary files /dev/null and b/lib/xmlbeans-5.1.1.jar differ diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..614b18d --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1796 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..22841c7 --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=070afaed +build.xml.script.CRC32=b80da75b +build.xml.stylesheet.CRC32=f85dc8f2@1.106.0.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=070afaed +nbproject/build-impl.xml.script.CRC32=7415c0b1 +nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.106.0.48 diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties new file mode 100644 index 0000000..1f2f166 --- /dev/null +++ b/nbproject/private/private.properties @@ -0,0 +1,8 @@ +compile.on.save=true +do.depend=false +do.jar=true +do.jlink=false +javac.debug=true +javadoc.preview=true +jlink.strip=false +user.properties.file=C:\\Users\\Ega\\AppData\\Roaming\\NetBeans\\17\\build.properties diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml new file mode 100644 index 0000000..66fe32f --- /dev/null +++ b/nbproject/private/private.xml @@ -0,0 +1,15 @@ + + + + + + file:/D:/Projects/Personal/verifone/utms-agent/src/id/iptek/utms/agent/Main.java + file:/D:/Projects/Personal/verifone/utms-agent/conf/mqtt-agents.cfg + file:/D:/Projects/Personal/verifone/utms-agent/src/id/iptek/utms/agent/sample/MqttPublishSample.java + + + file:/F:/Projects/Personal/verifone/utms-agent/src/id/iptek/utms/agent/Main.java + file:/F:/Projects/Personal/verifone/utms-agent/src/id/iptek/utms/agent/model/ApplicationSimple.java + + + diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..5f1d566 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,132 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=utms-agent +application.vendor=jakar +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.modulepath=\ + ${run.modulepath} +debug.test.classpath=\ + ${run.test.classpath} +debug.test.modulepath=\ + ${run.test.modulepath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/utms-agent.jar +dist.javadoc.dir=${dist.dir}/javadoc +dist.jlink.dir=${dist.dir}/jlink +dist.jlink.output=${dist.jlink.dir}/utms-agent +endorsed.classpath= +excludes= +file.reference.commons-codec-1.15.jar=lib\\commons-codec-1.15.jar +file.reference.commons-collections4-4.4.jar=lib\\commons-collections4-4.4.jar +file.reference.commons-compress-1.23.0.jar=lib\\commons-compress-1.23.0.jar +file.reference.commons-id.jar=lib\\commons-id.jar +file.reference.commons-io-2.13.0.jar=lib\\commons-io-2.13.0.jar +file.reference.commons-lang-2.0.jar=lib\\commons-lang-2.0.jar +file.reference.commons-math3-3.6.1.jar=lib\\commons-math3-3.6.1.jar +file.reference.gson-2.8.9.jar=lib\\gson-2.8.9.jar +file.reference.log4j-1.2.14.jar=lib\\log4j-1.2.14.jar +file.reference.log4j-api-2.20.0.jar=lib\\log4j-api-2.20.0.jar +file.reference.log4j-core-2.20.0.jar=lib\\log4j-core-2.20.0.jar +file.reference.log4j-rolling-appender-20120206-1607-1.2.15.jar=lib\\log4j-rolling-appender-20120206-1607-1.2.15.jar +file.reference.postgresql-42.2.9.jar=lib\\postgresql-42.2.9.jar +file.reference.slf4j-api-1.7.2.jar=lib\\slf4j-api-1.7.2.jar +file.reference.slf4j-log4j12-1.7.2.jar=lib\\slf4j-log4j12-1.7.2.jar +file.reference.xmlbeans-5.1.1.jar=lib\\xmlbeans-5.1.1.jar +includes=** +jar.compress=false +javac.classpath=\ + ${libs.Mqtt.classpath}:\ + ${file.reference.postgresql-42.2.9.jar}:\ + ${file.reference.log4j-1.2.14.jar}:\ + ${file.reference.log4j-api-2.20.0.jar}:\ + ${file.reference.log4j-core-2.20.0.jar}:\ + ${file.reference.log4j-rolling-appender-20120206-1607-1.2.15.jar}:\ + ${file.reference.slf4j-api-1.7.2.jar}:\ + ${file.reference.slf4j-log4j12-1.7.2.jar}:\ + ${file.reference.gson-2.8.9.jar}:\ + ${file.reference.commons-codec-1.15.jar}:\ + ${file.reference.commons-collections4-4.4.jar}:\ + ${file.reference.commons-compress-1.23.0.jar}:\ + ${file.reference.commons-io-2.13.0.jar}:\ + ${file.reference.commons-math3-3.6.1.jar}:\ + ${file.reference.xmlbeans-5.1.1.jar}:\ + ${file.reference.commons-id.jar}:\ + ${file.reference.commons-lang-2.0.jar}:\ + ${libs.Quartz.classpath}:\ + ${libs.Minio-Client.classpath} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=true +javac.modulepath= +javac.processormodulepath= +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.modulepath=\ + ${javac.modulepath} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.html5=false +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +# The jlink additional root modules to resolve +jlink.additionalmodules= +# The jlink additional command line parameters +jlink.additionalparam= +jlink.launcher=true +jlink.launcher.name=utms-agent +main.class=id.iptek.utms.agent.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.modulepath=\ + ${javac.modulepath} +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +run.test.modulepath=\ + ${javac.test.modulepath} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..92d1220 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,18 @@ + + + org.netbeans.modules.java.j2seproject + + + utms-agent + + + + + + + + + .\lib\nblibraries.properties + + + diff --git a/src/id/iptek/utms/agent/AppConfig.java b/src/id/iptek/utms/agent/AppConfig.java new file mode 100644 index 0000000..4e80d89 --- /dev/null +++ b/src/id/iptek/utms/agent/AppConfig.java @@ -0,0 +1,113 @@ +package id.iptek.utms.agent; + +import id.iptek.utms.agent.util.IOUtil; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Properties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class AppConfig { + + private static AppConfig self = null; + private Logger logger = LoggerFactory.getLogger(AppConfig.class); + private Properties props = new Properties(); + + private AppConfig() { + } + + public void init(String configFile) throws Exception { + init(new File(configFile)); + } + + public void init(File configFile) throws Exception { + InputStream in = new FileInputStream(configFile); + try { + props.load(in); + } finally { + IOUtil.close(in); + } + } + + public void set(String name, String value) { + props.setProperty(name, value); + } + + public String get(String name, String defaultValue) { + String value = get(name); + if(value == null) { + value = defaultValue; + } + return value; + } + + public String get(String name) { + logger.trace("#get('{}');", name); + String value = props.getProperty(name); + return value; + } + + public int getAsInt(String name) { + String value = get(name); + if(value != null) { + return Integer.parseInt(value); + } + return 0; + } + + public int getAsInt(String name, int defaultValue) { + String value = get(name); + if(value != null) { + return Integer.parseInt(value); + } + return defaultValue; + } + + public boolean getAsBoolean(String name) { + String value = get(name); + if(value != null) { + return "true".equalsIgnoreCase(value); + } + return false; + } + + public boolean getAsBoolean(String name, boolean defaultValue) { + String value = get(name); + if(value != null) { + return "true".equalsIgnoreCase(value); + } + return defaultValue; + } + + public long getAsLong(String name) { + return getAsLong(name, 0L); + } + + public long getAsLong(String name, long defaultValue) { + String value = get(name); + if(value != null) { + return Long.parseLong(value); + } + return defaultValue; + } + + public String[] getAll(String name) { + String value = get(name); + if(value != null) { + return value.trim().split("\\s*,\\s*"); + } + return new String[] {}; + } + + public Properties getProps() { + return props; + } + + public static AppConfig getInstance() { + if (self == null) { + self = new AppConfig(); + } + return self; + } +} diff --git a/src/id/iptek/utms/agent/DiagnosticFileCache.java b/src/id/iptek/utms/agent/DiagnosticFileCache.java new file mode 100644 index 0000000..a7b2d92 --- /dev/null +++ b/src/id/iptek/utms/agent/DiagnosticFileCache.java @@ -0,0 +1,117 @@ +package id.iptek.utms.agent; + +import id.iptek.utms.agent.model.FileReaderCacheObj; +import id.iptek.utms.agent.model.FileWriterCacheObj; +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public final class DiagnosticFileCache { + + protected Logger logger = LoggerFactory.getLogger(DiagnosticFileCache.class); + private static DiagnosticFileCache self = null; + private FileWriterCacheObj writerObj; + private FileReaderCacheObj readerObj; + private String fileBaseDir = null; + private int maxLinePerFile = 1000; + private DiagnosticFileChangeListener listener; + private final Object lock = new Object(); + + private DiagnosticFileCache() { + } + + public void setListener(DiagnosticFileChangeListener listener) { + this.listener = listener; + } + + public void setFileBaseDir(String fileBaseDir) { + this.fileBaseDir = fileBaseDir; + } + + public File startWriter(File file) throws IOException { + synchronized (lock) { + // there is previous writer, return old file + File previousFile = null; + if (writerObj != null) { + previousFile = writerObj.getFile(); + } else { + writerObj = new FileWriterCacheObj(); + } + writerObj.pointToFile(file); + return previousFile; + } + } + + public void setMaxLinePerFile(int maxLinePerFile) { + this.maxLinePerFile = maxLinePerFile; + } + + public int getMaxLinePerFile() { + return maxLinePerFile; + } + + public FileWriterCacheObj getWriter() { + return writerObj; + } + + public FileWriterCacheObj writeLine(String line) throws IOException { + synchronized (lock) { + if (writerObj == null) { + logger.debug("Create new WriterObj!!"); + // create new file + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + File newFile = new File(fileBaseDir, sdf.format(new Date()) + UUID.randomUUID().toString() + ".tmp"); + File oldFile = startWriter(newFile); + logger.debug("Pointed to file: \"{}\"", newFile.getAbsolutePath()); + if (listener != null) { + listener.fileChanged(oldFile, newFile); + } + writerObj.writeLine(line); + return writerObj; + } + if (!writerObj.writeLineWithMaxLineNo(maxLinePerFile, line)) { + // create new file + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + File newFile = new File(fileBaseDir, sdf.format(new Date()) + UUID.randomUUID().toString() + ".tmp"); + File oldFile = startWriter(newFile); + logger.debug("Pointed to file: \"{}\"", newFile.getAbsolutePath()); + if (listener != null) { + listener.fileChanged(oldFile, newFile); + } + // write the un-written line above + writerObj.writeLine(line); + return writerObj; + } + return writerObj; + } + } + + public void setReaderObj(FileReaderCacheObj readerObj) { + this.readerObj = readerObj; + } + + public FileReaderCacheObj getReaderObj() { + return readerObj; + } + + public static DiagnosticFileCache getInstance() { + if(self == null) { + self = new DiagnosticFileCache(); + } + return self; + } + + public interface DiagnosticFileChangeListener { + + void fileChanged(File oldFile, File newFile); + + } +} diff --git a/src/id/iptek/utms/agent/GenericCache.java b/src/id/iptek/utms/agent/GenericCache.java new file mode 100644 index 0000000..417dbd1 --- /dev/null +++ b/src/id/iptek/utms/agent/GenericCache.java @@ -0,0 +1,41 @@ +package id.iptek.utms.agent; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author jakar + */ +public final class GenericCache { + + private final static Map caches = new HashMap<>(); + private final Map cache = new HashMap<>(); + + private GenericCache() {} + + public void put(String name, Object value) { + cache.put(name, value); + } + + public Object get(String name) { + return cache.get(name); + } + + public void clear() { + cache.clear(); + } + + public int size() { + return cache.size(); + } + + public static GenericCache getCache(String name) { + GenericCache cache = caches.get(name); + if(cache == null) { + cache = new GenericCache(); + caches.put(name, cache); + } + return cache; + } +} diff --git a/src/id/iptek/utms/agent/HeartbeatFileCache.java b/src/id/iptek/utms/agent/HeartbeatFileCache.java new file mode 100644 index 0000000..4a13d97 --- /dev/null +++ b/src/id/iptek/utms/agent/HeartbeatFileCache.java @@ -0,0 +1,117 @@ +package id.iptek.utms.agent; + +import id.iptek.utms.agent.model.FileReaderCacheObj; +import id.iptek.utms.agent.model.FileWriterCacheObj; +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public final class HeartbeatFileCache { + + protected Logger logger = LoggerFactory.getLogger(HeartbeatFileCache.class); + private static HeartbeatFileCache self = null; + private FileWriterCacheObj writerObj; + private FileReaderCacheObj readerObj; + private String fileBaseDir = null; + private int maxLinePerFile = 1000; + private HeartbeatFileChangeListener listener; + private final Object lock = new Object(); + + private HeartbeatFileCache() { + } + + public void setListener(HeartbeatFileChangeListener listener) { + this.listener = listener; + } + + public void setFileBaseDir(String fileBaseDir) { + this.fileBaseDir = fileBaseDir; + } + + public File startWriter(File file) throws IOException { + synchronized (lock) { + // there is previous writer, return old file + File previousFile = null; + if (writerObj != null) { + previousFile = writerObj.getFile(); + } else { + writerObj = new FileWriterCacheObj(); + } + writerObj.pointToFile(file); + return previousFile; + } + } + + public void setMaxLinePerFile(int maxLinePerFile) { + this.maxLinePerFile = maxLinePerFile; + } + + public int getMaxLinePerFile() { + return maxLinePerFile; + } + + public FileWriterCacheObj getWriter() { + return writerObj; + } + + public FileWriterCacheObj writeLine(String line) throws IOException { + synchronized (lock) { + if (writerObj == null) { + logger.debug("Create new WriterObj!!"); + // create new file + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + File newFile = new File(fileBaseDir, sdf.format(new Date()) + UUID.randomUUID().toString() + ".tmp"); + File oldFile = startWriter(newFile); + logger.debug("Pointed to file: \"{}\"", newFile.getAbsolutePath()); + if (listener != null) { + listener.fileChanged(oldFile, newFile); + } + writerObj.writeLine(line); + return writerObj; + } + if (!writerObj.writeLineWithMaxLineNo(maxLinePerFile, line)) { + // create new file + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + File newFile = new File(fileBaseDir, sdf.format(new Date()) + UUID.randomUUID().toString() + ".tmp"); + File oldFile = startWriter(newFile); + logger.debug("Pointed to file: \"{}\"", newFile.getAbsolutePath()); + if (listener != null) { + listener.fileChanged(oldFile, newFile); + } + // write the un-written line above + writerObj.writeLine(line); + return writerObj; + } + return writerObj; + } + } + + public void setReaderObj(FileReaderCacheObj readerObj) { + this.readerObj = readerObj; + } + + public FileReaderCacheObj getReaderObj() { + return readerObj; + } + + public static HeartbeatFileCache getInstance() { + if(self == null) { + self = new HeartbeatFileCache(); + } + return self; + } + + public interface HeartbeatFileChangeListener { + + void fileChanged(File oldFile, File newFile); + + } +} diff --git a/src/id/iptek/utms/agent/Main.java b/src/id/iptek/utms/agent/Main.java new file mode 100644 index 0000000..34c04f3 --- /dev/null +++ b/src/id/iptek/utms/agent/Main.java @@ -0,0 +1,429 @@ +package id.iptek.utms.agent; + +import id.iptek.utms.agent.dao.TerminalDao; +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.model.TerminalIdObj; +import id.iptek.utms.agent.queue.ConsumerMode; +import id.iptek.utms.agent.queue.DelayMessageQueue; +import id.iptek.utms.agent.queue.DeviceInitQueueMessageHandler; +import id.iptek.utms.agent.queue.DiagnosticInfoQueueMessageHandler; +import id.iptek.utms.agent.queue.HeartBeatQueueMessageHandler; +import id.iptek.utms.agent.scheduler.job.DownloadTaskSchedulerJob; +import id.iptek.utms.agent.util.Singleton; +import id.iptek.utms.agent.worker.Worker; +import id.iptek.utms.agent.worker.WorkerFactory; +import io.minio.MinioClient; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.PropertyConfigurator; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import static org.quartz.JobBuilder.newJob; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerFactory; +import org.quartz.SimpleScheduleBuilder; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public class Main { + + private Logger logger = LoggerFactory.getLogger(Main.class); + private List workers; + private List queueHandlers; + private ExecutorService executorService = Executors.newCachedThreadPool(); + private final CountDownLatch shutdownLatch = new CountDownLatch(1); + private final AtomicBoolean stopping = new AtomicBoolean(false); + private static final long QUEUE_SHUTDOWN_TIMEOUT_SECONDS = 30L; + + public void start(String[] args) { + String configFile = getConfigFile(args); + File config = new File(configFile); + File configParent = config.getAbsoluteFile().getParentFile(); + String baseDir = configParent != null && configParent.getParentFile() != null ? configParent.getParentFile().getAbsolutePath() : "."; + if (baseDir == null) { + baseDir = "."; + } + String configDir = (new StringBuilder()).append(baseDir).append(File.separator).append("conf").toString(); + AppConfig appConfig = AppConfig.getInstance(); + //init config + try { + initConfig(appConfig, baseDir, configDir, configFile); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + //init logging + try { + initLogging(appConfig); + } catch (Exception ex) { + logger.debug("Error initializing Logging: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + //init db + try { + initDB(appConfig); + } catch (Exception ex) { + logger.error("Error initializing DB: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + //init mqtt client + try { + initMqttClient(appConfig); + } catch (Exception ex) { + logger.error("Error init Mqtt Client: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + //init minio client + try { + initMinioClient(appConfig); + } catch (Exception ex) { + logger.error("Error init Minio Client: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + //load data async + try { + loadDataAsync(appConfig); + } catch (Exception ex) { + logger.error("Error load data async: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + //build message queue handlers + try { + buildQueueHandlers(appConfig); + } catch (Exception ex) { + logger.error("Error build & start queue handlers: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + //build & start workers + try { + buildAndStartWorkers(appConfig); + } catch (Exception ex) { + logger.error("Error build & start workers: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + //start schedulers + try { + startSchedulers(appConfig); + } catch (Exception ex) { + logger.error("Error starting schedulers: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + } + + private String getConfigFile(String[] args) { + if (args == null || args.length == 0) { + throw new IllegalArgumentException("Usage: java -jar utms-agent.jar "); + } + if (args.length > 1 && Main.class.getName().equals(args[0])) { + return args[1]; + } + return args[0]; + } + + private void initConfig(AppConfig appConfig, String baseDir, String configDir, String configFile) throws Exception { + File _configFile = new File(configFile); + /*if(!_configFile.isAbsolute()) { + _configFile = new File(configDir, configFile); + }*/ + appConfig.init(_configFile); + appConfig.set("Config.File", _configFile.getAbsolutePath()); + appConfig.set("Base.Dir", baseDir); + appConfig.set("Config.Dir", configDir); + } + + private void initLogging(AppConfig appConfig) throws Exception { + String configFile = appConfig.get("log4j.file"); + String configDir = appConfig.get("Config.Dir"); + configFile = configFile.replace("${Config.Dir}", configDir); + BasicConfigurator.resetConfiguration(); + PropertyConfigurator.configure(configFile); + logger.info("Config file: {}", appConfig.get("Config.File")); + logger.info("Base dir: {}", appConfig.get("Base.Dir")); + logger.info("Config dir: {}", configDir); + logger.info("Logging config file: {}", configFile); + logger.info("Logging initialized."); + } + + private void initDB(AppConfig appConfig) throws Exception { + logger.info("Init DB ..."); + String datasource = appConfig.get("db.datasource"); + String driver = appConfig.get("db.driver"); + String url = appConfig.get("db.url"); + String user = appConfig.get("db.user"); + String password = appConfig.get("db.password"); + + if (datasource != null && !"".equals(datasource.trim())) { + logger.debug("Using datasource."); + DBConn.getInstance().init(datasource); + } else { + logger.debug("Using traditional url, username, and password."); + DBConn.getInstance().init(driver, url, user, password); + } + logger.info("DB initialized."); + } + + private void initMqttClient(AppConfig appConfig) throws Exception { + logger.info("Init Mqtt client ..."); + String clientId = appConfig.get("mqtt.clientid_prefix") + RandomStringUtils.randomAlphanumeric(10); + String brokerUrl = appConfig.get("mqtt.broker.url"); + String username = appConfig.get("mqtt.user"); + String password = appConfig.get("mqtt.password"); + int keepAlive = appConfig.getAsInt("mqtt.keepalive", 10); + int maxInFlight = appConfig.getAsInt("mqtt.maxinflight", 32); + boolean cleanSession = appConfig.getAsBoolean("mqtt.cleansession", true); + boolean autoReconnect = appConfig.getAsBoolean("mqtt.autoreconnect", true); + + MemoryPersistence persistence = new MemoryPersistence(); + MqttClient mqttClient = new MqttClient(brokerUrl, clientId, persistence); + MqttConnectOptions connOpts = new MqttConnectOptions(); + connOpts.setCleanSession(cleanSession); + connOpts.setUserName(username); + connOpts.setPassword(password.toCharArray()); + connOpts.setAutomaticReconnect(autoReconnect); + connOpts.setKeepAliveInterval(keepAlive); + connOpts.setMaxInflight(maxInFlight); + logger.info("Try to connect ..."); + mqttClient.connect(connOpts); + + // put into Singleton + Singleton.getInstance("MQTT_CLIENT").setObject(mqttClient); + logger.info("Mqtt client initialized."); + } + + private void initMinioClient(AppConfig appConfig) throws Exception { + MinioClient minioClient + = MinioClient.builder() + .endpoint(appConfig.get("fileserver.endpoint")) + .credentials(appConfig.get("fileserver.access_key"), appConfig.get("fileserver.secret_key")) + .build(); + // put into Singleton + Singleton.getInstance("MINIO_CLIENT").setObject(minioClient); + } + + private void buildAndStartWorkers(AppConfig appConfig) throws Exception { + logger.info("Build & Start workers ..."); + workers = WorkerFactory.getInstance().buildWorkers(appConfig); + for (Worker worker : workers) { + logger.info("Start worker .. '{}'", worker.getLoggingId()); + worker.setExecutorService(executorService); + worker.start(); + logger.info("Worker started"); + executorService.execute(worker); + } + logger.info("Workers build & started."); + } + + private void buildQueueHandlers(AppConfig appConfig) throws Exception { + queueHandlers = new ArrayList<>(); + + logger.info("Build & Start queue handlers ..."); + // heartbeat queue handler + logger.info("Start Heartbeat queue handler .. "); + DelayMessageQueue hbQueue = new DelayMessageQueue(executorService, new HeartBeatQueueMessageHandler(), 10); + hbQueue.setMode(ConsumerMode.BATCH); + hbQueue.setTotalThread(3); + hbQueue.run(); + // put to singleton + Singleton.getInstance(HeartBeatQueueMessageHandler.NAME).setObject(hbQueue); + queueHandlers.add(hbQueue); + logger.info("Heartbeat queue handler started"); + + // diagnostic queue handler + logger.info("Start Diagnostic queue handler .. "); + DelayMessageQueue diagQueue = new DelayMessageQueue(executorService, new DiagnosticInfoQueueMessageHandler(), 3); + diagQueue.setMode(ConsumerMode.BATCH); + diagQueue.setTotalThread(2); + diagQueue.run(); + // put to singleton + Singleton.getInstance(DiagnosticInfoQueueMessageHandler.NAME).setObject(diagQueue); + queueHandlers.add(diagQueue); + logger.info("Diagnostic queue handler started"); + + // init queue handler + logger.info("Start Init queue handler .. "); + DelayMessageQueue initQueue = new DelayMessageQueue(executorService, new DeviceInitQueueMessageHandler(), 3); + initQueue.setMode(ConsumerMode.BATCH); + initQueue.setTotalThread(2); + initQueue.run(); + // put to singleton + Singleton.getInstance(DeviceInitQueueMessageHandler.NAME).setObject(initQueue); + queueHandlers.add(initQueue); + logger.info("Init queue handler started"); + + logger.info("Queue handlers build & started."); + } + + private void loadDataAsync(AppConfig appConfig) throws Exception { + logger.info("Load data async ..."); + // load terminal Id Generic Cache + loadTerminalIdCache(); + logger.info("End of load data async."); + } + + private void loadTerminalIdCache() throws Exception { + executorService.submit(new Runnable() { + @Override + public void run() { + try { + logger.debug("Load Terminal Id data ..."); + TerminalDao terminalDao = new TerminalDao(); + GenericCache cache = GenericCache.getCache("terminalId"); + + List terminalList = terminalDao.getTerminalIds(); + terminalList.forEach((t) -> { + cache.put(t.getSn().toLowerCase(), t.getTerminalId()); + }); + logger.debug("Terminal Id data loaded: {} data.", terminalList.size()); + } catch(Exception ex) { + logger.error("Error loading Terminal Id cache: {}", ex.getMessage(), ex); + } + } + }); + } + + private void startSchedulers(AppConfig appConfig) throws Exception { + logger.info("Start schedulers ..."); + // download task scheduler + JobDetail job = newJob(DownloadTaskSchedulerJob.class) + .withIdentity("downloadTaskScheduler", "task") + .build(); + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity("myTrigger", "task") + .startNow() + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withIntervalInMinutes(appConfig.getAsInt("scheduler.periode.minutes", 5)) + .repeatForever()) + .build(); + + SchedulerFactory sf = new StdSchedulerFactory(); + Scheduler sched = sf.getScheduler(); + sched.scheduleJob(job, trigger); + sched.start(); + + Singleton.getInstance("SCHEDULER").setObject(sched); + logger.info("Schedulers started."); + } + + private void stopQueueHandlers() throws Exception { + logger.info("Stop queue handlers ..."); + if (queueHandlers == null) { + logger.info("Queue handlers have not been started."); + return; + } + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(QUEUE_SHUTDOWN_TIMEOUT_SECONDS); + for (DelayMessageQueue queue : queueHandlers) { + try { + logger.info("Stop queue handler .. '{}'", queue.getClass()); + long remainingNanos = deadline - System.nanoTime(); + if (remainingNanos <= 0L) { + logger.warn("Queue graceful shutdown timeout reached, forcing queue handler '{}'", queue.getClass()); + queue.stop(true); + } else { + boolean graceful = queue.stop(false, remainingNanos, TimeUnit.NANOSECONDS); + if (!graceful) { + logger.warn("Force queue handler .. '{}'", queue.getClass()); + queue.stop(true); + } + } + logger.info("Queue handler stopped"); + } catch (Exception ex) { + logger.error("Error stopping queue handler: {}", ex.getMessage(), ex); + } + } + logger.info("Queue handlers stopped."); + } + + private void stopWorkers() throws Exception { + logger.info("Stop workers ..."); + if (workers == null) { + logger.info("Workers have not been started."); + return; + } + for (Worker worker : workers) { + try { + logger.info("Stop worker .. '{}'", worker.getLoggingId()); + worker.stop(); + logger.info("Worker stopped"); + } catch (Exception ex) { + logger.error("Error stopping worker '{}': {}", worker.getLoggingId(), ex.getMessage(), ex); + } + } + logger.info("Workers stopped."); + } + + public void stop() { + if (stopping.compareAndSet(false, true)) { + try { + stopScheduler(); + stopQueueHandlers(); + stopWorkers(); + stopMqttClient(); + executorService.shutdownNow(); + } catch (Exception ex) { + logger.error("Error stopping: {}", ex.getMessage(), ex); + } finally { + shutdownLatch.countDown(); + } + } + } + + private void stopMqttClient() throws Exception { + Object object = Singleton.getInstance("MQTT_CLIENT").getObject(); + if (object instanceof MqttClient) { + MqttClient mqttClient = (MqttClient) object; + if (mqttClient.isConnected()) { + logger.info("Disconnect MQTT client ..."); + mqttClient.disconnectForcibly(0L, 250L, false); + logger.info("MQTT client disconnected."); + } + mqttClient.close(true); + } + } + + private void stopScheduler() throws Exception { + Object scheduler = Singleton.getInstance("SCHEDULER").getObject(); + if (scheduler instanceof Scheduler) { + logger.info("Stop scheduler ..."); + ((Scheduler) scheduler).shutdown(false); + logger.info("Scheduler stopped."); + } + } + + private void awaitShutdown() throws InterruptedException { + shutdownLatch.await(); + } + + public static void main(String[] args) { + final Main main = new Main(); + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + main.stop(); + } + }, "utms-agent-shutdown")); + + try { + main.start(args); + main.awaitShutdown(); + } catch (Exception ex) { + main.stop(); + System.exit(1); + } + } +} diff --git a/src/id/iptek/utms/agent/dao/ApplicationDao.java b/src/id/iptek/utms/agent/dao/ApplicationDao.java new file mode 100644 index 0000000..10e68de --- /dev/null +++ b/src/id/iptek/utms/agent/dao/ApplicationDao.java @@ -0,0 +1,76 @@ +package id.iptek.utms.agent.dao; + +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.db.DBUtil; +import id.iptek.utms.agent.db.DatabaseException; +import id.iptek.utms.agent.model.ApplicationExt; +import id.iptek.utms.agent.model.PendingDownloadTask; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; + +/** + * + * @author Jaka + */ +public class ApplicationDao { + + public boolean updateIconUrl(ApplicationExt app) throws DatabaseException { + boolean saved = false; + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + pstmt = conn.prepareStatement("" + + "update tms_application set icon_url=?,icon_url_exp=? where id=?::uuid"); + int idx = 1; + pstmt.setString(idx++, app.getIconUrl()); + pstmt.setTimestamp(idx++, new Timestamp(app.getIconUrlExp().getTime())); + pstmt.setString(idx++, app.getId()); + saved = pstmt.executeUpdate() == 1; + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(pstmt); + DBUtil.close(conn); + } + return saved; + } + + public boolean updateAppUrl(PendingDownloadTask task, ApplicationExt app) throws DatabaseException { + boolean saved = false; + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + pstmt = conn.prepareStatement("" + + "update tms_download_task_application_link set download_url=?,download_url_exp=? where download_task_id=?::uuid and application_id=?::uuid"); + int idx = 1; + pstmt.setString(idx++, app.getDownloadUrl()); + pstmt.setTimestamp(idx++, new Timestamp(app.getDownloadUrlExp().getTime())); + pstmt.setString(idx++, task.getId()); + pstmt.setString(idx++, app.getId()); + saved = pstmt.executeUpdate() == 1; + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(pstmt); + DBUtil.close(conn); + } + return saved; + } + +} diff --git a/src/id/iptek/utms/agent/dao/DeleteTaskDao.java b/src/id/iptek/utms/agent/dao/DeleteTaskDao.java new file mode 100644 index 0000000..ac1945b --- /dev/null +++ b/src/id/iptek/utms/agent/dao/DeleteTaskDao.java @@ -0,0 +1,256 @@ +package id.iptek.utms.agent.dao; + +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.db.DBUtil; +import id.iptek.utms.agent.db.DatabaseException; +import id.iptek.utms.agent.model.ApplicationSimple; +import id.iptek.utms.agent.model.PendingDeleteTask; +import id.iptek.utms.agent.model.TaskAckReq; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jaka + */ +public class DeleteTaskDao { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + public DeleteTaskDao() { + } + + public List getPendingTaskForOnlineTerminals(int minutes, int limit) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " terminal.sn, " + + " task.id,task.\"name\",task.delete_time, " + + " app.id app_id,app.app_name,app.package_name, " + + " log.id log_id " + + "from public.tms_delete_task task " + + " inner join public.tms_delete_task_log log on log.task_id=task.id " + + " inner join public.tms_terminal terminal on log.terminal_id=terminal.id and terminal.delete_ts is null and terminal.heartbeat_status=1 " + + " inner join public.tms_delete_task_application_simple_link link on link.delete_task_id=task.id and link.application_simple_id=log.application_simple_id " + + " inner join public.tms_application_simple app on link.application_simple_id=app.id " + + "where log.activity=0 and (log.last_broadcast_ts is null or (log.last_broadcast_ts < current_timestamp - interval '"+minutes+" minutes')) " + + "order by task.create_ts desc,log.last_broadcast_ts asc " + + "limit ? offset 0"); + pstmt.setInt(1, limit); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDeleteTask task = new PendingDeleteTask(); + task.setTerminalSN(rs.getString(1)); + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDeleteTime(rs.getTimestamp(4)); + ApplicationSimple app = new ApplicationSimple(); + app.setId(rs.getString(5)); + app.setAppName(rs.getString(6)); + app.setPackageName(rs.getString(7)); + task.setApp(app); + task.setLogId(rs.getString(8)); + list.add(task); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + public List getPendingTaskForTerminals(int minutes, int limit) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " terminal.sn, " + + " task.id,task.\"name\",task.delete_time, " + + " app.id app_id,app.app_name,app.package_name, " + + " log.id log_id " + + "from public.tms_delete_task task " + + " inner join public.tms_delete_task_log log on log.task_id=task.id " + + " inner join public.tms_terminal terminal on log.terminal_id=terminal.id and terminal.delete_ts is null " + + " inner join public.tms_delete_task_application_simple_link link on link.delete_task_id=task.id and link.application_simple_id=log.application_simple_id " + + " inner join public.tms_application_simple app on link.application_simple_id=app.id " + + "where log.activity=0 and (log.last_broadcast_ts is null or (log.last_broadcast_ts < current_timestamp - interval '"+minutes+" minutes')) " + + "order by task.create_ts desc,log.last_broadcast_ts asc " + + "limit ? offset 0"); + pstmt.setInt(1, limit); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDeleteTask task = new PendingDeleteTask(); + task.setTerminalSN(rs.getString(1)); + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDeleteTime(rs.getTimestamp(4)); + ApplicationSimple app = new ApplicationSimple(); + app.setId(rs.getString(5)); + app.setAppName(rs.getString(6)); + app.setPackageName(rs.getString(7)); + task.setApp(app); + task.setLogId(rs.getString(8)); + list.add(task); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + public List getPendingTaskForTerminal(String terminalSN) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " terminal.sn, " + + " task.id,task.\"name\",task.delete_time, " + + " app.id app_id,app.app_name,app.package_name, " + + " log.id log_id " + + "from public.tms_delete_task task " + + " inner join public.tms_delete_task_log log on log.task_id=task.id " + + " inner join public.tms_terminal terminal on log.terminal_id=terminal.id and terminal.delete_ts is null " + + " inner join public.tms_delete_task_application_simple_link link on link.delete_task_id=task.id and link.application_simple_id=log.application_simple_id " + + " inner join public.tms_application_simple app on link.application_simple_id=app.id " + + "where lower(terminal.sn)=? " + + " and log.activity=0"); + pstmt.setString(1, terminalSN.toLowerCase()); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDeleteTask task = new PendingDeleteTask(); + task.setTerminalSN(rs.getString(1)); + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDeleteTime(rs.getTimestamp(4)); + ApplicationSimple app = new ApplicationSimple(); + app.setId(rs.getString(5)); + app.setAppName(rs.getString(6)); + app.setPackageName(rs.getString(7)); + task.setApp(app); + task.setLogId(rs.getString(8)); + list.add(task); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + public boolean saveLastBroadcastTs(PendingDeleteTask task) throws DatabaseException { + boolean saved = false; + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + pstmt = conn.prepareStatement("" + + "update tms_delete_task_log set last_broadcast_ts=NOW() where id=?::uuid"); + pstmt.setString(1, task.getLogId()); + int affectedRows = pstmt.executeUpdate(); + saved = affectedRows == 1; + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, null); + } + return saved; + } + + public boolean save(TaskAckReq ackReq) throws DatabaseException { + boolean saved = false; + Connection conn = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + // update download task log activity + saved |= updateDeleteTaskLogActivity(conn, ackReq); + if (saved) { + int totalUnfinished = getUnfinishedTask(conn, ackReq); + if (totalUnfinished == 0) { + //logger.debug("Total unfinished is 0, set to DONE"); + //boolean taskStatusUpdated = updateDownloadTaskLogStatus(conn, ackReq); + //logger.debug("Download Task status updated? {}", taskStatusUpdated); + } + } + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn); + } + return saved; + } + + private boolean updateDeleteTaskLogActivity(Connection conn, TaskAckReq ackReq) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_delete_task_log set activity=?,message=?,update_ts=NOW(),updated_by=? where id=?::uuid"); + int idx = 1; + pstmt.setInt(idx++, ackReq.getActivity()); + pstmt.setString(idx++, ackReq.getMessage()); + pstmt.setString(idx++, ackReq.getTerminalSN()); + pstmt.setString(idx++, ackReq.getAckId()); + saved = pstmt.executeUpdate() >= 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private int getUnfinishedTask(Connection conn, TaskAckReq ackReq) throws DatabaseException { + PreparedStatement pstmt = null; + ResultSet rs = null; + int total = 0; + try { + pstmt = conn.prepareStatement("" + + "select count(*) from public.tms_delete_task_log " + + "where task_id = (select id from tms_delete_task_log where id = ?::uuid) " + + " and activity not in (3,4)"); + pstmt.setString(1, ackReq.getAckId()); + rs = pstmt.executeQuery(); + if (rs.next()) { + total = rs.getInt(1); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(rs); + DBUtil.close(pstmt); + } + return total; + } + +} diff --git a/src/id/iptek/utms/agent/dao/DiagnosticDao.java b/src/id/iptek/utms/agent/dao/DiagnosticDao.java new file mode 100644 index 0000000..7bc1acf --- /dev/null +++ b/src/id/iptek/utms/agent/dao/DiagnosticDao.java @@ -0,0 +1,296 @@ +package id.iptek.utms.agent.dao; + +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.db.DBUtil; +import id.iptek.utms.agent.db.DatabaseException; +import id.iptek.utms.agent.model.ApplicationSimple; +import id.iptek.utms.agent.model.CellInfo; +import id.iptek.utms.agent.model.Diagnostic; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +/** + * + * @author Jaka + */ +public class DiagnosticDao { + + public DiagnosticDao() { + } + + public boolean save(Diagnostic diagnostic) throws DatabaseException { + boolean saved = false; + Connection conn = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + // save heartbeat first + saved |= saveDiagnostic(conn, diagnostic); + if (saved) { + saved |= updateTerminalLastDiagnostic(conn, diagnostic); + } + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn); + } + return saved; + } + + public boolean saveAll(List diagnostics) throws DatabaseException { + boolean saved = false; + Connection conn = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + // save heartbeat first + saved |= saveDiagnostics(conn, diagnostics); + if (saved) { + saved |= updateAllTerminalLastDiagnostic(conn, diagnostics); + } + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn); + } + return saved; + } + + private boolean saveDiagnostic(Connection conn, Diagnostic diagnostic) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "insert into tms_diagnostic_info (id,sn,battery_temp,battery_percentage,latitude,longitude," + + " meid,switching_times,swiping_card_times,dip_inserting_times,nfc_card_reading_times, " + + " front_camera_open_times,rear_camera_open_times,charge_times, " + + " total_memory,available_memory,total_flash_memory,available_flash_memory,total_mobile_data, " + + " current_boot_time,total_boot_time,installed_apps_string,total_length_printed, " + + " cell_name, cell_type, cell_strength, " + + " create_ts,created_by,version) " + + "values (?::uuid,?,?,?,?,?," + + " ?,?,?,?,?, " + + " ?,?,?, " + + " ?,?,?,?,?, " + + " ?,?,?,?, " + + " ?,?,?, " + + " ?,?,1) "); + int idx = 1; + pstmt.setString(idx++, diagnostic.getId()); + pstmt.setString(idx++, diagnostic.getDeviceSn()); + pstmt.setDouble(idx++, diagnostic.getDiagnosticInfo().getBatteryTemp()); + pstmt.setDouble(idx++, diagnostic.getDiagnosticInfo().getBatteryPercentage()); + pstmt.setDouble(idx++, diagnostic.getLocationInfo().getLat()); + pstmt.setDouble(idx++, diagnostic.getLocationInfo().getLng()); + pstmt.setString(idx++, diagnostic.getDiagnosticInfo().getMeid()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getSwitchingTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getSwipingCardTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getDipInsertingTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getNfcCardReadingTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getFrontCameraOpenTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getRearCameraOpenTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getChargeTimes()); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getTotalMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getAvailableMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getTotalFlashMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getAvailableFlashMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getTotalMobileData())); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getCurrentBootTime()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getTotalBootTime()); + pstmt.setString(idx++, diagnostic.getDiagnosticInfo().getInstalledAppString()); + pstmt.setDouble(idx++, diagnostic.getDiagnosticInfo().getTotalLengthPrinted()); + if(diagnostic.getCellInfo() != null && !diagnostic.getCellInfo().isEmpty()) { + CellInfo cellInfo = diagnostic.getCellInfo().get(0); + pstmt.setString(idx++, cellInfo.getName()); + pstmt.setString(idx++, cellInfo.getType()); + pstmt.setInt(idx++, cellInfo.getStrength()); + } else { + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.INTEGER); + } + pstmt.setTimestamp(idx++, new java.sql.Timestamp(diagnostic.getCreateTime().getTime())); + pstmt.setString(idx++, "system"); + + saved = pstmt.executeUpdate() == 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private boolean saveDiagnostics(Connection conn, List diagnostics) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "insert into tms_diagnostic_info (id,sn,battery_temp,battery_percentage,latitude,longitude," + + " meid,switching_times,swiping_card_times,dip_inserting_times,nfc_card_reading_times, " + + " front_camera_open_times,rear_camera_open_times,charge_times, " + + " total_memory,available_memory,total_flash_memory,available_flash_memory,total_mobile_data, " + + " current_boot_time,total_boot_time,installed_apps_string,total_length_printed, " + + " cell_name, cell_type, cell_strength, " + + " create_ts,created_by,version) " + + "values (?::uuid,?,?,?,?,?," + + " ?,?,?,?,?, " + + " ?,?,?, " + + " ?,?,?,?,?, " + + " ?,?,?,?, " + + " ?,?,?, " + + " ?,?,1) "); + for (Diagnostic diagnostic : diagnostics) { + int idx = 1; + pstmt.setString(idx++, diagnostic.getId()); + pstmt.setString(idx++, diagnostic.getDeviceSn()); + pstmt.setDouble(idx++, diagnostic.getDiagnosticInfo().getBatteryTemp()); + pstmt.setDouble(idx++, diagnostic.getDiagnosticInfo().getBatteryPercentage()); + pstmt.setDouble(idx++, diagnostic.getLocationInfo().getLat()); + pstmt.setDouble(idx++, diagnostic.getLocationInfo().getLng()); + pstmt.setString(idx++, diagnostic.getDiagnosticInfo().getMeid()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getSwitchingTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getSwipingCardTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getDipInsertingTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getNfcCardReadingTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getFrontCameraOpenTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getRearCameraOpenTimes()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getChargeTimes()); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getTotalMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getAvailableMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getTotalFlashMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getAvailableFlashMemory())); + pstmt.setLong(idx++, getLong(diagnostic.getDiagnosticInfo().getTotalMobileData())); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getCurrentBootTime()); + pstmt.setInt(idx++, diagnostic.getDiagnosticInfo().getTotalBootTime()); + pstmt.setString(idx++, diagnostic.getDiagnosticInfo().getInstalledAppString()); + pstmt.setDouble(idx++, diagnostic.getDiagnosticInfo().getTotalLengthPrinted()); + if(diagnostic.getCellInfo() != null && !diagnostic.getCellInfo().isEmpty()) { + CellInfo cellInfo = diagnostic.getCellInfo().get(0); + pstmt.setString(idx++, cellInfo.getName()); + pstmt.setString(idx++, cellInfo.getType()); + pstmt.setInt(idx++, cellInfo.getStrength()); + } else { + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.INTEGER); + } + pstmt.setTimestamp(idx++, new java.sql.Timestamp(diagnostic.getCreateTime().getTime())); + pstmt.setString(idx++, "system"); + pstmt.addBatch(); + pstmt.clearParameters(); + } + int[] xs = pstmt.executeBatch(); + int affectedRows = 0; + for (int x : xs) { + affectedRows += x; + } + saved = affectedRows == diagnostics.size(); + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private boolean updateTerminalLastDiagnostic(Connection conn, Diagnostic diagnostic) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + String appVersion = null; + String launcherVersion = null; + String vfsVersion = null; + String vfssVersion = null; + for (ApplicationSimple app : diagnostic.getInstalledApps()) { + if ("com.briit.brimobile".equals(app.getPackageName())) { + appVersion = app.getVersion(); + } + if ("com.unified.launcher".equals(app.getPackageName())) { + launcherVersion = app.getVersion(); + } + if ("com.vfi.smartpos.system_service".equals(app.getPackageName())) { + vfsVersion = app.getVersion(); + } + if ("com.vfi.smartpos.deviceservice".equals(app.getPackageName())) { + vfssVersion = app.getVersion(); + } + } + + pstmt = conn.prepareStatement("" + + "update tms_last_diagnostic_info set diagnostic_info_id=?::uuid,update_ts=now() where terminal_sn=?"); + pstmt.setString(1, diagnostic.getId()); + pstmt.setString(2, diagnostic.getDeviceSn()); + saved = pstmt.executeUpdate() == 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private boolean updateAllTerminalLastDiagnostic(Connection conn, List diagnostics) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_last_diagnostic_info set diagnostic_info_id=?::uuid,update_ts=now() where terminal_sn=?"); + for (Diagnostic diagnostic : diagnostics) { + String appVersion = null; + String launcherVersion = null; + String vfsVersion = null; + String vfssVersion = null; + for (ApplicationSimple app : diagnostic.getInstalledApps()) { + if ("com.briit.brimobile".equals(app.getPackageName())) { + appVersion = app.getVersion(); + } + if ("com.unified.launcher".equals(app.getPackageName())) { + launcherVersion = app.getVersion(); + } + if ("com.vfi.smartpos.system_service".equals(app.getPackageName())) { + vfsVersion = app.getVersion(); + } + if ("com.vfi.smartpos.deviceservice".equals(app.getPackageName())) { + vfssVersion = app.getVersion(); + } + } + + pstmt.setString(1, diagnostic.getId()); + pstmt.setString(2, diagnostic.getDeviceSn()); + pstmt.addBatch(); + pstmt.clearParameters(); + } + int[] xs = pstmt.executeBatch(); + int affectedRows = 0; + for (int x : xs) { + affectedRows += x; + } + saved = affectedRows > 0; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private long getLong(Object val) { + if (val instanceof String) { + String sVal = (String) val; + if (sVal.contains("B")) { + sVal = sVal.replace("B", ""); + } + return Long.parseLong(sVal.trim()); + } else if (val instanceof Long) { + return (Long) val; + } else if (val instanceof Double) { + return ((Double) val).longValue(); + } + return 0; + } +} diff --git a/src/id/iptek/utms/agent/dao/DownloadTaskDao.java b/src/id/iptek/utms/agent/dao/DownloadTaskDao.java new file mode 100644 index 0000000..d514359 --- /dev/null +++ b/src/id/iptek/utms/agent/dao/DownloadTaskDao.java @@ -0,0 +1,1013 @@ +package id.iptek.utms.agent.dao; + +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.db.DBUtil; +import id.iptek.utms.agent.db.DatabaseException; +import id.iptek.utms.agent.model.ApplicationExt; +import id.iptek.utms.agent.model.DownloadTask; +import id.iptek.utms.agent.model.PendingDownloadTask; +import id.iptek.utms.agent.model.TaskAckReq; +import id.iptek.utms.agent.model.enumeration.TaskStatus; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jaka + */ +public class DownloadTaskDao { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + public DownloadTaskDao() { + } + + /* + public Date getCurrentTime() throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + Date currenTime = null; + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select now();"); + rs = pstmt.executeQuery(); + if (rs.next()) { + currenTime = rs.getTimestamp(1); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return currenTime; + } + + private boolean updateDownloadTaskLogStatus(Connection conn, TaskAckReq ackReq) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_download_task set status=4,update_ts=NOW(),updated_by=? where id=((select task_id from tms_download_task_log where id = ?::uuid))"); + int idx = 1; + pstmt.setString(idx++, ackReq.getTerminalSN()); + pstmt.setString(idx++, ackReq.getAckId()); + saved = pstmt.executeUpdate() >= 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + public List getPendingTaskForTerminal(String terminalSN) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " terminal.sn, " + + " task.id,task.\"name\",task.download_time_type,task.download_time, " + + " task.installation_time_type,task.installation_time,task.installation_notification, " + + " log.id log_id, " + + " app.id app_id,app.\"name\" app_name,app.package_name,app.app_version,app.company_name, " + + " app.uninstallable,app.description,app.apk_id, " + + " file.id file_id,file.ext,file.create_date, " + + " app.checksum,app.download_url " + + "from public.tms_download_task task " + + " inner join public.tms_download_task_log log on log.task_id=task.id " + + " inner join public.tms_terminal terminal on log.terminal_id=terminal.id and terminal.delete_ts is null " + + " inner join public.tms_terminal_group_link tglink on tglink.terminal_id=terminal.id " + + " inner join public.tms_download_task_terminal_group_link glink on glink.download_task_id=task.id and glink.group_id=tglink.terminal_group_id " + + " inner join public.tms_download_task_application_link link on link.download_task_id=task.id and link.application_id=log.application_id " + + " inner join public.tms_application app on link.application_id=app.id " + + " inner join public.sys_file file on app.apk_id=file.id " + + "where lower(terminal.sn)=? " + + " and log.activity=0"); + pstmt.setString(1, terminalSN.toLowerCase()); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDownloadTask task = new PendingDownloadTask(); + task.setTerminalSN(rs.getString(1)); + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDownloadTimeType(rs.getInt(4)); + task.setDownloadTime(rs.getTimestamp(5)); + task.setInstallationTimeType(rs.getInt(6)); + task.setInstallationTime(rs.getTimestamp(7)); + task.setInstallationNotification(rs.getInt(8)); + task.setLogId(rs.getString(9)); + Application app = new Application(); + app.setId(rs.getString(10)); + app.setAppName(rs.getString(11)); + app.setPackageName(rs.getString(12)); + app.setVersion(rs.getString(13)); + app.setCompanyName(rs.getString(14)); + app.setUninstallable(rs.getBoolean(15)); + app.setDescription(rs.getString(16)); + app.setApkId(rs.getString(17)); + app.setFileId(rs.getString(18)); + app.setFileExt(rs.getString(19)); + app.setFileCreateDate(rs.getDate(20)); + app.setChecksum(rs.getString(21)); + app.setDownloadUrl(rs.getString(22)); + task.setApp(app); + list.add(task); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + public List getPendingTaskForOnlineTerminals(int minutes, int limit) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " terminal.sn, " + + " task.id,task.\"name\",task.download_time_type,task.download_time, " + + " task.installation_time_type,task.installation_time,task.installation_notification, " + + " log.id log_id, " + + " app.id app_id,app.\"name\" app_name,app.package_name,app.app_version,app.company_name, " + + " app.uninstallable,app.description,app.apk_id, " + + " file.id file_id,file.ext,file.create_date, " + + " app.checksum,app.download_url " + + "from public.tms_download_task task " + + " inner join public.tms_download_task_log log on log.task_id=task.id " + + " inner join public.tms_terminal terminal on log.terminal_id=terminal.id and terminal.delete_ts is null and terminal.heartbeat_status=1 " + + " inner join public.tms_terminal_group_link tglink on tglink.terminal_id=terminal.id " + + " inner join public.tms_download_task_terminal_group_link glink on glink.download_task_id=task.id and glink.group_id=tglink.terminal_group_id " + + " inner join public.tms_download_task_application_link link on link.download_task_id=task.id and link.application_id=log.application_id " + + " inner join public.tms_application app on link.application_id=app.id " + + " inner join public.sys_file file on app.apk_id=file.id " + + "where (task.broadcast is null or task.broadcast=false) and log.activity=0 and (log.last_broadcast_ts is null or (log.last_broadcast_ts < current_timestamp - interval '"+minutes+" minutes')) " + + "order by task.create_ts desc,log.last_broadcast_ts asc " + + "limit ? offset 0"); + pstmt.setInt(1, limit); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDownloadTask task = new PendingDownloadTask(); + task.setTerminalSN(rs.getString(1)); + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDownloadTimeType(rs.getInt(4)); + task.setDownloadTime(rs.getTimestamp(5)); + task.setInstallationTimeType(rs.getInt(6)); + task.setInstallationTime(rs.getTimestamp(7)); + task.setInstallationNotification(rs.getInt(8)); + task.setLogId(rs.getString(9)); + Application app = new Application(); + app.setId(rs.getString(10)); + app.setAppName(rs.getString(11)); + app.setPackageName(rs.getString(12)); + app.setVersion(rs.getString(13)); + app.setCompanyName(rs.getString(14)); + app.setUninstallable(rs.getBoolean(15)); + app.setDescription(rs.getString(16)); + app.setApkId(rs.getString(17)); + app.setFileId(rs.getString(18)); + app.setFileExt(rs.getString(19)); + app.setFileCreateDate(rs.getDate(20)); + app.setChecksum(rs.getString(21)); + app.setDownloadUrl(rs.getString(22)); + task.setApp(app); + list.add(task); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + public List getPendingTaskForTerminals(int minutes, int limit) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " terminal.sn, " + + " task.id,task.\"name\",task.download_time_type,task.download_time, " + + " task.installation_time_type,task.installation_time,task.installation_notification, " + + " log.id log_id, " + + " app.id app_id,app.\"name\" app_name,app.package_name,app.app_version,app.company_name, " + + " app.uninstallable,app.description,app.apk_id, " + + " file.id file_id,file.ext,file.create_date, " + + " app.checksum,app.download_url " + + "from public.tms_download_task task " + + " inner join public.tms_download_task_log log on log.task_id=task.id " + + " inner join public.tms_terminal terminal on log.terminal_id=terminal.id and terminal.delete_ts is null " + + " inner join public.tms_terminal_group_link tglink on tglink.terminal_id=terminal.id " + + " inner join public.tms_download_task_terminal_group_link glink on glink.download_task_id=task.id and glink.group_id=tglink.terminal_group_id " + + " inner join public.tms_download_task_application_link link on link.download_task_id=task.id and link.application_id=log.application_id " + + " inner join public.tms_application app on link.application_id=app.id " + + " inner join public.sys_file file on app.apk_id=file.id " + + "where (task.broadcast is null or task.broadcast=false) and log.activity=0 and (log.last_broadcast_ts is null or (log.last_broadcast_ts < now() - interval '"+minutes+" minutes')) " + + "order by task.create_ts desc,log.last_broadcast_ts asc " + + "limit ? offset 0"); + pstmt.setInt(1, limit); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDownloadTask task = new PendingDownloadTask(); + task.setTerminalSN(rs.getString(1)); + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDownloadTimeType(rs.getInt(4)); + task.setDownloadTime(rs.getTimestamp(5)); + task.setInstallationTimeType(rs.getInt(6)); + task.setInstallationTime(rs.getTimestamp(7)); + task.setInstallationNotification(rs.getInt(8)); + task.setLogId(rs.getString(9)); + Application app = new Application(); + app.setId(rs.getString(10)); + app.setAppName(rs.getString(11)); + app.setPackageName(rs.getString(12)); + app.setVersion(rs.getString(13)); + app.setCompanyName(rs.getString(14)); + app.setUninstallable(rs.getBoolean(15)); + app.setDescription(rs.getString(16)); + app.setApkId(rs.getString(17)); + app.setFileId(rs.getString(18)); + app.setFileExt(rs.getString(19)); + app.setFileCreateDate(rs.getDate(20)); + app.setChecksum(rs.getString(21)); + app.setDownloadUrl(rs.getString(22)); + task.setApp(app); + list.add(task); + } + logger.debug("Total Pending Size: {}", list.size()); + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + public PendingDownloadTask getNextPendingTaskForBroadcast() throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + PendingDownloadTask task = null; + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " 'SN' sn, " + + " task.id,task.\"name\",task.download_time_type,task.download_time, " + + " task.installation_time_type,task.installation_time,task.installation_notification, " + + " 'BROADCAST' log_id, " + + " app.id app_id,app.\"name\" app_name,app.package_name,app.app_version,app.company_name, " + + " app.uninstallable,app.description,app.apk_id, " + + " file.id file_id,file.ext,file.create_date, " + + " app.checksum,app.download_url " + + "from public.tms_download_task task " + + " inner join public.tms_download_task_log log on log.task_id=task.id " + + " inner join public.tms_download_task_application_link link on link.download_task_id=task.id and link.application_id=log.application_id " + + " inner join public.tms_application app on link.application_id=app.id " + + " inner join public.sys_file file on app.apk_id=file.id " + + "where task.broadcast=true and task.status not in (4, 6, 7, 99) " + + "order by task.create_ts asc " + + "limit 1 offset 0"); + rs = pstmt.executeQuery(); + if (rs.next()) { + task = new PendingDownloadTask(); + task.setTerminalSN(rs.getString(1)); // dummy SN + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDownloadTimeType(rs.getInt(4)); + task.setDownloadTime(rs.getTimestamp(5)); + task.setInstallationTimeType(rs.getInt(6)); + task.setInstallationTime(rs.getTimestamp(7)); + task.setInstallationNotification(rs.getInt(8)); + task.setLogId(rs.getString(9)); // dummy log id + Application app = new Application(); + app.setId(rs.getString(10)); + app.setAppName(rs.getString(11)); + app.setPackageName(rs.getString(12)); + app.setVersion(rs.getString(13)); + app.setCompanyName(rs.getString(14)); + app.setUninstallable(rs.getBoolean(15)); + app.setDescription(rs.getString(16)); + app.setApkId(rs.getString(17)); + app.setFileId(rs.getString(18)); + app.setFileExt(rs.getString(19)); + app.setFileCreateDate(rs.getDate(20)); + app.setChecksum(rs.getString(21)); + app.setDownloadUrl(rs.getString(22)); + task.setApp(app); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return task; + } + + public boolean saveLastBroadcastTs(PendingDownloadTask task) throws DatabaseException { + boolean saved = false; + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + pstmt = conn.prepareStatement("" + + "update tms_download_task_log set last_broadcast_ts=NOW() where id=?::uuid"); + pstmt.setString(1, task.getLogId()); + int affectedRows = pstmt.executeUpdate(); + saved = affectedRows == 1; + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, null); + } + return saved; + } + + public List getPendingTasksForBroadcast() throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " task.id,task.\"name\" " + + "from public.tms_download_task task " + + "where task.status not in (4, 6, 7, 99) " + + "order by task.create_ts asc"); + rs = pstmt.executeQuery(); + while (rs.next()) { + SimplePendingDownloadTask task = new SimplePendingDownloadTask(); + task.setId(rs.getString(1)); + task.setName(rs.getString(2)); + task.setGroupIds(getTaskGroupIds(conn, task)); + list.add(task); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + private List getTaskGroupIds(Connection conn, SimplePendingDownloadTask task) throws DatabaseException { + PreparedStatement pstmt = null; + ResultSet rs = null; + List groupIds = new ArrayList<>(); + try { + pstmt = conn.prepareStatement("" + + "select tgl.group_id " + + "from public.tms_download_task task " + + " inner join public.tms_download_task_terminal_group_link tgl on tgl.download_task_id=task.id " + + "where task.id = ?::uuid"); + pstmt.setString(1, task.getId()); + rs = pstmt.executeQuery(); + while (rs.next()) { + groupIds.add(rs.getString(1)); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(rs); + DBUtil.close(pstmt); + } + return groupIds; + } + + public PendingDownloadTask getPendingTask(String taskId, String terminalId) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + PendingDownloadTask task = null; + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " 'SN' sn, " + + " task.id,task.\"name\",task.download_time_type,task.download_time, " + + " task.installation_time_type,task.installation_time,task.installation_notification, " + + " log.id log_id, " + + " app.id app_id,app.\"name\" app_name,app.package_name,app.app_version,app.company_name, " + + " app.uninstallable,app.description,app.apk_id, " + + " file.id file_id,file.ext,file.create_date, " + + " app.checksum,app.download_url " + + "from public.tms_download_task task " + + " inner join public.tms_download_task_log log on log.task_id=task.id " + + " inner join public.tms_download_task_application_link link on link.download_task_id=task.id and link.application_id=log.application_id " + + " inner join public.tms_application app on link.application_id=app.id " + + " inner join public.sys_file file on app.apk_id=file.id " + + "where task.id=?::uuid and log.terminal_id=?::uuid"); + pstmt.setString(1, taskId); + pstmt.setString(2, terminalId); + rs = pstmt.executeQuery(); + if (rs.next()) { + task = new PendingDownloadTask(); + task.setTerminalSN(rs.getString(1)); // dummy SN + task.setId(rs.getString(2)); + task.setName(rs.getString(3)); + task.setDownloadTimeType(rs.getInt(4)); + task.setDownloadTime(rs.getTimestamp(5)); + task.setInstallationTimeType(rs.getInt(6)); + task.setInstallationTime(rs.getTimestamp(7)); + task.setInstallationNotification(rs.getInt(8)); + task.setLogId(rs.getString(9)); // dummy log id + Application app = new Application(); + app.setId(rs.getString(10)); + app.setAppName(rs.getString(11)); + app.setPackageName(rs.getString(12)); + app.setVersion(rs.getString(13)); + app.setCompanyName(rs.getString(14)); + app.setUninstallable(rs.getBoolean(15)); + app.setDescription(rs.getString(16)); + app.setApkId(rs.getString(17)); + app.setFileId(rs.getString(18)); + app.setFileExt(rs.getString(19)); + app.setFileCreateDate(rs.getDate(20)); + app.setChecksum(rs.getString(21)); + app.setDownloadUrl(rs.getString(22)); + task.setApp(app); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return task; + }*/ + + + /** + * Get Unscheduled Tasks + * @return + * @throws DatabaseException + */ + public List getUnscheduledTasks() throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List tasks = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select task.id,task.\"name\",\n" + + " task.publish_time_type,task.publish_time,\n" + + " task.download_time_type,task.download_time,\n" + + " task.installation_time_type,task.installation_time,task.installation_notification,\n" + + " task.status,task.tenant_id,task.create_ts,task.\"version\" \n" + + "from tms_download_task task\n" + + "where task.delete_ts is null and task.status in (0,1,2) \n" + + " and (task.publish_time_type=1 \n" + + " or (task.publish_time_type=2 and now() >= (task.publish_time - interval '1 hour'))\n" + + " )\n" + + "order by task.publish_time_type asc, task.publish_time asc"); + rs = pstmt.executeQuery(); + while (rs.next()) { + DownloadTask task = new DownloadTask(); + task.setId(rs.getString(1)); + task.setName(rs.getString(2)); + task.setPublishTimeType(rs.getInt(3)); + Timestamp ts = rs.getTimestamp(4); + if(!rs.wasNull()) { + task.setPublishTime(ts); + } + task.setDownloadTimeType(rs.getInt(5)); + ts = rs.getTimestamp(6); + if(!rs.wasNull()) { + task.setDownloadTime(ts); + } + task.setInstallationNotification(rs.getInt(7)); + ts = rs.getTimestamp(8); + if(!rs.wasNull()) { + task.setDownloadTime(ts); + } + task.setInstallationNotification(rs.getInt(9)); + task.setStatus(rs.getInt(10)); + tasks.add(task); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return tasks; + } + + public List getTaskApplications(DownloadTask task) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List apps = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "SELECT app.id,app.\"name\",app.package_name,app.app_version,\n " + + " app.company_name,app.uninstallable,app.description,app.checksum,\n " + + " app.unique_name,applink.download_url,applink.download_url_exp,\n " + + " app.unique_icon_name,app.icon_url,app.icon_url_exp\n " + + "FROM \"public\".tms_application app\n " + + " INNER JOIN \"public\".tms_download_task_application_link applink on applink.application_id=app.id\n " + + " INNER JOIN \"public\".tms_download_task task on applink.download_task_id=task.id\n " + + "WHERE task.id=?::uuid "); + pstmt.setString(1, task.getId()); + rs = pstmt.executeQuery(); + while (rs.next()) { + ApplicationExt app = new ApplicationExt(); + app.setId(rs.getString(1)); + app.setAppName(rs.getString(2)); + app.setPackageName(rs.getString(3)); + app.setVersion(rs.getString(4)); + app.setCompanyName(rs.getString(5)); + app.setUninstallable(rs.getBoolean(6)); + app.setDescription(rs.getString(7)); + app.setChecksum(rs.getString(8)); + app.setUniqueName(rs.getString(9)); + app.setDownloadUrl(rs.getString(10)); + Date downloadUrlExp = rs.getDate(11); + if(!rs.wasNull()) { + app.setDownloadUrlExp(downloadUrlExp); + } + app.setUniqueIconName(rs.getString(12)); + app.setIconUrl(rs.getString(13)); + Date iconUrlExp = rs.getDate(14); + if(!rs.wasNull()) { + app.setIconUrlExp(iconUrlExp); + } + apps.add(app); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return apps; + } + + public List getFinalTerminalIdForTask(DownloadTask task) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List terminalIds = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "SELECT DISTINCT t0.terminal_id \n" + + "FROM (\n" + + " SELECT tlink.terminal_id\n" + + " FROM \"public\".tms_download_task_terminal_link tlink\n" + + " WHERE tlink.download_task_id=?::uuid\n" + + " UNION ALL\n" + + " SELECT tglink.terminal_id\n" + + " FROM \"public\".tms_download_task_terminal_group_link ttglink\n" + + " INNER JOIN \"public\".tms_terminal_group tgroup on ttglink.group_id=tgroup.id\n" + + " INNER JOIN \"public\".tms_terminal_group_link tglink on tglink.terminal_group_id=tgroup.id\n" + + " WHERE ttglink.download_task_id=?::uuid\n" + + ") t0\n"); + pstmt.setString(1, task.getId()); + pstmt.setString(2, task.getId()); + rs = pstmt.executeQuery(); + while (rs.next()) { + terminalIds.add(rs.getString(1)); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return terminalIds; + } + + private boolean updateDownloadTaskStatus(Connection conn, DownloadTask task) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_download_task set status=? where id=?::uuid"); + int idx = 1; + pstmt.setInt(idx++, task.getStatus().getValue()); + pstmt.setString(idx++, task.getId()); + saved = pstmt.executeUpdate() == 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private boolean populateDownloadTask(Connection conn, DownloadTask task) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "INSERT INTO \"public\".tms_download_task_log (id,create_ts,created_by,\"version\",task_id, \n" + + " application_id,activity,terminal_id,terminal_group_id) \n" + + "SELECT t1.id,t1.create_ts,t1.created_by,t1.\"version\", \n" + + " t1.task_id,t1.application_id,t1.activity,t1.terminal_id,t1.terminal_group_id \n" + + "FROM ( \n" + + " SELECT t0.id,t0.create_ts,t0.created_by,t0.\"version\", \n" + + " t0.task_id,t0.application_id,t0.activity,t0.terminal_id,t0.terminal_group_id, \n" + + " row_number() over (partition by t0.task_id,t0.terminal_id order by t0.terminal_group_id asc) as rownumber \n" + + " FROM ( \n" + + " -- mapped to terminal group \n" + + " SELECT \n" + + " newid() id,now() create_ts,'utms-agent' created_by,1 \"version\", \n" + + " task.id task_id,app.id application_id, \n" + + " 0 activity,tgl.terminal_id,tgl.terminal_group_id \n" + + " FROM \"public\".tms_application app \n" + + " INNER JOIN \"public\".tms_download_task_application_link applink on applink.application_id=app.id \n" + + " INNER JOIN \"public\".tms_download_task task on applink.download_task_id=task.id \n" + + " INNER JOIN \"public\".tms_download_task_terminal_group_link dttgl on task.id = dttgl.download_task_id \n" + + " INNER JOIN \"public\".tms_terminal_group_link tgl on dttgl.group_id = tgl.terminal_group_id \n" + + " WHERE task.id=?::uuid and task.status=1 \n" + + " UNION ALL \n" + + " -- mapped to terminal \n" + + " SELECT \n" + + " newid() id,now() create_ts,'utms-agent' created_by,1 \"version\", \n" + + " task.id task_id,app.id application_id, \n" + + " 0 activity,dttl.terminal_id,null terminal_group_id \n" + + " FROM \"public\".tms_application app \n" + + " INNER JOIN \"public\".tms_download_task_application_link applink on applink.application_id=app.id \n" + + " INNER JOIN \"public\".tms_download_task task on applink.download_task_id=task.id \n" + + " INNER JOIN \"public\".tms_download_task_terminal_link dttl on task.id = dttl.download_task_id \n" + + " WHERE task.id=?::uuid and task.status=1 \n" + + " ) t0 \n" + + ") t1 \n" + + "WHERE t1.rownumber=1; \n" + ); + pstmt.setString(1, task.getId()); + pstmt.setString(2, task.getId()); + saved = pstmt.executeUpdate() > 0; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + public List getUnpublishedPendingTaskByTerminal(DownloadTask task) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List tasks = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "SELECT log.id, \n" + + " log.task_id,task.\"name\" task_name, \n" + + " task.download_time_type,task.download_time, \n" + + " task.installation_time_type,task.installation_time,task.installation_notification, \n" + + " log.activity, \n" + + " log.terminal_id,terminal.sn,log.terminal_group_id, \n" + + " log.application_id app_id,app.\"name\" app_name,app.package_name, \n" + + " app.app_version,app.company_name,app.uninstallable,app.description, \n" + + " app.checksum,app.unique_name,app.unique_icon_name,app.icon_url,app.icon_url_exp, \n" + + " app.file_size,applink.download_url,applink.download_url_exp, \n" + + " log.last_broadcast_ts \n" + + "FROM \"public\".tms_download_task_log log \n" + + " INNER JOIN \"public\".tms_download_task task on log.task_id = task.id \n" + + " INNER JOIN \"public\".tms_download_task_application_link applink on log.task_id = applink.download_task_id \n" + + " INNER JOIN \"public\".tms_terminal terminal on log.terminal_id = terminal.id \n" + + " INNER JOIN \"public\".tms_application app on log.application_id = app.id \n" + + "WHERE log.task_id=?::uuid and log.activity=0 and log.terminal_group_id is null; \n" + ); + pstmt.setString(1, task.getId()); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDownloadTask pendingTask = new PendingDownloadTask(); + pendingTask.setLogId(rs.getString(1)); + pendingTask.setId(rs.getString(2)); + pendingTask.setName(rs.getString(3)); + pendingTask.setDownloadTimeType(rs.getInt(4)); + Timestamp ts = rs.getTimestamp(5); + if(!rs.wasNull()) { + pendingTask.setDownloadTime(ts); + } + pendingTask.setInstallationTimeType(rs.getInt(6)); + ts = rs.getTimestamp(7); + if(!rs.wasNull()) { + pendingTask.setInstallationTime(ts); + } + pendingTask.setInstallationNotification(rs.getInt(8)); + pendingTask.setActivity(rs.getInt(9)); + // 10: terminal id + pendingTask.setTerminalSN(rs.getString(11)); + pendingTask.setTerminalGroupId(rs.getString(12)); + // application + ApplicationExt app = new ApplicationExt(); + app.setId(rs.getString(13)); + app.setAppName(rs.getString(14)); + app.setPackageName(rs.getString(15)); + app.setVersion(rs.getString(16)); + app.setCompanyName(rs.getString(17)); + app.setUninstallable(rs.getBoolean(18)); + app.setDescription(rs.getString(19)); + app.setChecksum(rs.getString(20)); + app.setUniqueName(rs.getString(21)); + app.setUniqueIconName(rs.getString(22)); + app.setIconUrl(rs.getString(23)); + Date iconUrlExp = rs.getDate(24); + if(!rs.wasNull()) { + app.setIconUrlExp(iconUrlExp); + } + app.setFileSize(rs.getLong(25)); + app.setDownloadUrl(rs.getString(26)); + Date downloadUrlExp = rs.getDate(27); + if(!rs.wasNull()) { + app.setDownloadUrlExp(downloadUrlExp); + } + pendingTask.setApp(app); + Date lastBroadcastTs = rs.getDate(28); + if(!rs.wasNull()) { + pendingTask.setLastBroadcastTs(lastBroadcastTs); + } + tasks.add(pendingTask); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return tasks; + } + + public List getUnpublishedPendingTaskByTerminalGroup(DownloadTask task) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List tasks = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "SELECT t0.* from ( \n" + + " SELECT log.id,log.task_id,task.\"name\" task_name, \n" + + " task.download_time_type,task.download_time, \n" + + " task.installation_time_type,task.installation_time,task.installation_notification, \n" + + " log.terminal_group_id, \n" + + " log.application_id app_id,app.\"name\" app_name,app.package_name, \n" + + " app.app_version,app.company_name,app.uninstallable,app.description, \n" + + " app.checksum,app.unique_name,app.unique_icon_name,app.icon_url,app.icon_url_exp, \n" + + " app.file_size,applink.download_url,applink.download_url_exp, \n" + + " ttgl.last_broadcasted_time, \n" + + " row_number() over (partition by log.task_id,log.terminal_group_id order by ttgl.last_broadcasted_time asc) as rownumber \n" + + " FROM \"public\".tms_download_task_log log \n" + + " INNER JOIN \"public\".tms_download_task task on log.task_id = task.id \n" + + " INNER JOIN \"public\".tms_download_task_application_link applink on log.task_id = applink.download_task_id \n" + + " INNER JOIN \"public\".tms_download_task_terminal_group_link ttgl on log.task_id = ttgl.download_task_id and log.terminal_group_id = ttgl.group_id \n" + + " INNER JOIN \"public\".tms_application app on log.application_id = app.id \n" + + " WHERE log.task_id=?::uuid and log.activity=0 \n" + + " and log.terminal_group_id is not null \n" + + ") t0 \n" + + "where t0.rownumber=1; \n" + ); + pstmt.setString(1, task.getId()); + rs = pstmt.executeQuery(); + while (rs.next()) { + PendingDownloadTask pendingTask = new PendingDownloadTask(); + pendingTask.setLogId(rs.getString(1)); + pendingTask.setId(rs.getString(2)); + pendingTask.setName(rs.getString(3)); + pendingTask.setDownloadTimeType(rs.getInt(4)); + Timestamp ts = rs.getTimestamp(5); + if(!rs.wasNull()) { + pendingTask.setDownloadTime(ts); + } + pendingTask.setInstallationTimeType(rs.getInt(6)); + ts = rs.getTimestamp(7); + if(!rs.wasNull()) { + pendingTask.setInstallationTime(ts); + } + pendingTask.setInstallationNotification(rs.getInt(8)); + pendingTask.setTerminalGroupId(rs.getString(9)); + // application + ApplicationExt app = new ApplicationExt(); + app.setId(rs.getString(10)); + app.setAppName(rs.getString(11)); + app.setPackageName(rs.getString(12)); + app.setVersion(rs.getString(13)); + app.setCompanyName(rs.getString(14)); + app.setUninstallable(rs.getBoolean(15)); + app.setDescription(rs.getString(16)); + app.setChecksum(rs.getString(17)); + app.setUniqueName(rs.getString(18)); + app.setUniqueIconName(rs.getString(19)); + app.setIconUrl(rs.getString(20)); + Date iconUrlExp = rs.getDate(21); + if(!rs.wasNull()) { + app.setIconUrlExp(iconUrlExp); + } + app.setFileSize(rs.getLong(22)); + app.setDownloadUrl(rs.getString(23)); + Date downloadUrlExp = rs.getDate(24); + if(!rs.wasNull()) { + app.setDownloadUrlExp(downloadUrlExp); + } + pendingTask.setApp(app); + Date lastBroadcastTs = rs.getDate(25); + if(!rs.wasNull()) { + pendingTask.setLastBroadcastTs(lastBroadcastTs); + } + tasks.add(pendingTask); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return tasks; + } + + public boolean populateAndBuildDownloadTaskLogs(DownloadTask task) throws DatabaseException { + Connection conn = null; + CallableStatement cstmt = null; + ResultSet rs = null; + boolean saved = false; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + // update download task status + task.setStatus(TaskStatus.NOT_STARTED.getValue()); + saved = updateDownloadTaskStatus(conn, task); + logger.info("Update download task status: {} ? {}", task.getId(), saved); + if(saved) { + // populate task logs + saved &= populateDownloadTask(conn, task); + logger.info("Populate download task log: {} ? {}", task.getId(), saved); + } + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, cstmt, rs); + } + return saved; + } + + public boolean updateGroupLastBroadcastTs(PendingDownloadTask task) throws DatabaseException { + boolean saved = false; + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + pstmt = conn.prepareStatement("" + + "update tms_download_task_terminal_group_link set last_broadcasted_time=now() where download_task_id=?::uuid and group_id=?::uuid"); + int idx = 1; + pstmt.setString(idx++, task.getId()); + pstmt.setString(idx++, task.getTerminalGroupId()); + saved = pstmt.executeUpdate() == 1; + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(pstmt); + DBUtil.close(conn); + } + return saved; + } + + public boolean updateTerminalLastBroadcastTs(PendingDownloadTask task) throws DatabaseException { + boolean saved = false; + Connection conn = null; + PreparedStatement pstmt = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + pstmt = conn.prepareStatement("" + + "update tms_download_task_log set last_broadcast_ts=now() where id=?::uuid"); + int idx = 1; + pstmt.setString(idx++, task.getLogId()); + saved = pstmt.executeUpdate() == 1; + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(pstmt); + DBUtil.close(conn); + } + return saved; + } + + public boolean save(TaskAckReq ackReq) throws DatabaseException { + boolean saved = false; + Connection conn = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + // update download task log activity + if(!ackReq.isBroadcasted()) { + // sent to single device + saved |= updateDownloadTaskLogActivity(conn, ackReq); + } else { + // broadcasted to terminal group initially + saved |= updateDownloadTaskLogActivityByTaskAndTerminal(conn, ackReq); + } + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn); + } + return saved; + } + + private boolean updateDownloadTaskLogActivity(Connection conn, TaskAckReq ackReq) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_download_task_log set activity=?,message=?,update_ts=NOW(),updated_by=? where id=?::uuid"); + int idx = 1; + pstmt.setInt(idx++, ackReq.getActivity()); + pstmt.setString(idx++, ackReq.getMessage()); + pstmt.setString(idx++, ackReq.getTerminalSN()); + pstmt.setString(idx++, ackReq.getAckId()); + saved = pstmt.executeUpdate() == 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private boolean updateDownloadTaskLogActivityByTaskAndTerminal(Connection conn, TaskAckReq ackReq) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_download_task_log set old_activity=activity,activity=?,message=?,update_ts=NOW(),updated_by=? where task_id=?::uuid and terminal_id=?::uuid"); + int idx = 1; + pstmt.setInt(idx++, ackReq.getActivity()); + pstmt.setString(idx++, ackReq.getMessage()); + pstmt.setString(idx++, ackReq.getTerminalSN()); + pstmt.setString(idx++, ackReq.getTaskId()); + pstmt.setString(idx++, ackReq.getTerminalId()); + saved = pstmt.executeUpdate() >= 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private int getUnfinishedTask(Connection conn, TaskAckReq ackReq) throws DatabaseException { + PreparedStatement pstmt = null; + ResultSet rs = null; + int total = 0; + try { + pstmt = conn.prepareStatement("" + + "select count(*) from public.tms_download_task_log " + + "where task_id = (select id from tms_download_task_log where id = ?::uuid) " + + " and activity not in (4,6,7)"); + pstmt.setString(1, ackReq.getAckId()); + rs = pstmt.executeQuery(); + if (rs.next()) { + total = rs.getInt(1); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(rs); + DBUtil.close(pstmt); + } + return total; + } +} diff --git a/src/id/iptek/utms/agent/dao/HeartbeatDao.java b/src/id/iptek/utms/agent/dao/HeartbeatDao.java new file mode 100644 index 0000000..984c051 --- /dev/null +++ b/src/id/iptek/utms/agent/dao/HeartbeatDao.java @@ -0,0 +1,196 @@ +package id.iptek.utms.agent.dao; + +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.db.DBUtil; +import id.iptek.utms.agent.db.DatabaseException; +import id.iptek.utms.agent.model.CellInfo; +import id.iptek.utms.agent.model.HeartBeat; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jaka + */ +public class HeartbeatDao { + + private Logger logger = LoggerFactory.getLogger(HeartbeatDao.class); + + public HeartbeatDao() { + } + + public boolean save(HeartBeat hearbeat) throws DatabaseException { + boolean saved = false; + Connection conn = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + // save heartbeat first + saved |= saveHeartBeat(conn, hearbeat); + if (saved) { + saved |= updateTerminalLastHearbeat(conn, hearbeat); + } + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn); + } + return saved; + } + + public Connection getDBConnection() throws SQLException { + Connection conn = DBConn.getInstance().getConnection(); + return conn; + } + + public boolean saveHeartBeat(Connection conn, HeartBeat heartbeat) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "insert into tms_heart_beat (id,sn,battery_temp,battery_percentage,latitude,longitude,cell_name,cell_type,cell_strength,create_ts,created_by,version) " + + "values (?::uuid,?,?,?,?,?,?,?,?,?,?,1) "); + int idx = 1; + pstmt.setString(idx++, heartbeat.getId()); + pstmt.setString(idx++, heartbeat.getDeviceSn()); + pstmt.setDouble(idx++, heartbeat.getDiagnosticInfo().getBatteryTemp()); + pstmt.setDouble(idx++, heartbeat.getDiagnosticInfo().getBatteryPercentage()); + pstmt.setDouble(idx++, heartbeat.getLocationInfo().getLat()); + pstmt.setDouble(idx++, heartbeat.getLocationInfo().getLng()); + if(heartbeat.getCellInfo() != null && !heartbeat.getCellInfo().isEmpty()) { + CellInfo cellInfo = heartbeat.getCellInfo().get(0); + pstmt.setString(idx++, cellInfo.getName()); + pstmt.setString(idx++, cellInfo.getType()); + pstmt.setInt(idx++, cellInfo.getStrength()); + } else { + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.INTEGER); + } + pstmt.setTimestamp(idx++, new java.sql.Timestamp(heartbeat.getCreateTime().getTime())); + pstmt.setString(idx++, "system"); + //saved = pstmt.executeUpdate() == 1; + saved = pstmt.executeUpdate() >= 0; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + public boolean saveAll(List hearbeats) throws DatabaseException { + boolean saved = false; + Connection conn = null; + try { + conn = DBConn.getInstance().getConnection(); + conn.setAutoCommit(false); + + // save heartbeat first + saved |= saveHeartBeats(conn, hearbeats); + if (saved) { + saved |= updateTerminalLastHearbeats(conn, hearbeats); + } + + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException ex) { + DBUtil.rollback(conn); + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn); + } + return saved; + } + + private boolean saveHeartBeats(Connection conn, List heartbeats) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "insert into tms_heart_beat (id,sn,battery_temp,battery_percentage,latitude,longitude,cell_name,cell_type,cell_strength,create_ts,created_by,version) " + + "values (?::uuid,?,?,?,?,?,?,?,?,?,?,1) "); + for(HeartBeat heartbeat : heartbeats) { + int idx = 1; + pstmt.setString(idx++, heartbeat.getId()); + pstmt.setString(idx++, heartbeat.getDeviceSn()); + pstmt.setDouble(idx++, heartbeat.getDiagnosticInfo().getBatteryTemp()); + pstmt.setDouble(idx++, heartbeat.getDiagnosticInfo().getBatteryPercentage()); + pstmt.setDouble(idx++, heartbeat.getLocationInfo().getLat()); + pstmt.setDouble(idx++, heartbeat.getLocationInfo().getLng()); + if(heartbeat.getCellInfo() != null && !heartbeat.getCellInfo().isEmpty()) { + CellInfo cellInfo = heartbeat.getCellInfo().get(0); + pstmt.setString(idx++, cellInfo.getName()); + pstmt.setString(idx++, cellInfo.getType()); + pstmt.setInt(idx++, cellInfo.getStrength()); + } else { + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.VARCHAR); + pstmt.setNull(idx++, java.sql.Types.INTEGER); + } + pstmt.setTimestamp(idx++, new java.sql.Timestamp(heartbeat.getCreateTime().getTime())); + pstmt.setString(idx++, "system"); + pstmt.addBatch(); + pstmt.clearParameters(); + } + int[] xs = pstmt.executeBatch(); + int affectedRows = 0; + for(int x : xs) { + affectedRows += x; + } + //saved = affectedRows > 0; + saved = affectedRows >= 0; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private boolean updateTerminalLastHearbeat(Connection conn, HeartBeat heartbeat) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_last_heartbeat set heart_beat_id=?::uuid,update_ts=now() where terminal_sn=?"); + pstmt.setString(1, heartbeat.getId()); + pstmt.setString(2, heartbeat.getDeviceSn()); + saved = pstmt.executeUpdate() == 1; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + + private boolean updateTerminalLastHearbeats(Connection conn, List heartbeats) throws SQLException { + boolean saved = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("" + + "update tms_last_heartbeat set heart_beat_id=?::uuid,update_ts=now() where terminal_sn=?"); + for(HeartBeat heartbeat : heartbeats) { + pstmt.setString(1, heartbeat.getId()); + pstmt.setString(2, heartbeat.getDeviceSn()); + pstmt.addBatch(); + pstmt.clearParameters(); + } + int[] xs = pstmt.executeBatch(); + int affectedRows = 0; + for(int x : xs) { + affectedRows += x; + } + saved = affectedRows > 0; + } finally { + DBUtil.close(pstmt); + } + return saved; + } + +} diff --git a/src/id/iptek/utms/agent/dao/ProfileDao.java b/src/id/iptek/utms/agent/dao/ProfileDao.java new file mode 100644 index 0000000..f6213d3 --- /dev/null +++ b/src/id/iptek/utms/agent/dao/ProfileDao.java @@ -0,0 +1,111 @@ +package id.iptek.utms.agent.dao; + +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.db.DBUtil; +import id.iptek.utms.agent.db.DatabaseException; +import id.iptek.utms.agent.model.Profile; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Jaka + */ +public class ProfileDao { + + public Profile getProfileForTerminal(String terminalSN) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + Profile profile = null; + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select " + + " terminal.sn, " + + " profile.id profile_id,profile.\"name\",profile.heartbeat_interval,profile.diagnostic_interval, " + + " profile.mask_home_button,profile.mask_status_bar,profile.schedule_reboot,profile.schedule_reboot_time, " + + " profile.is_default,profile.relocation_alert,profile.moving_threshold,profile.admin_password,profile.front_app " + + "from public.tms_terminal terminal " + + " inner join public.tms_device_profile profile on terminal.profile_id=profile.id " + + "where lower(terminal.sn)=? and terminal.delete_ts is null;"); + pstmt.setString(1, terminalSN.toLowerCase()); + rs = pstmt.executeQuery(); + if (rs.next()) { + profile = new Profile(); + profile.setTerminalSN(rs.getString(1)); + profile.setId(rs.getString(2)); + profile.setName(rs.getString(3)); + profile.setHeartbeatInterval(rs.getInt(4)); + profile.setDiagnosticInterval(rs.getInt(5)); + profile.setMaskHomeButton(rs.getBoolean(6)); + profile.setMaskStatusButton(rs.getBoolean(7)); + profile.setScheduleReboot(rs.getBoolean(8)); + profile.setScheduleRebootTime(rs.getTimestamp(9)); + profile.setDefault(rs.getBoolean(10)); + profile.setRelocationAlert(rs.getBoolean(11)); + profile.setMovingThreshold(rs.getInt(12)); + profile.setAdminPassword(rs.getString(13)); + profile.setFrontApp(rs.getString(14)); + + profile.setApps(getProfileApps(conn, profile.getId())); + profile.setGroupIds(getTerminalGroupIds(conn, terminalSN)); + } + } catch (SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return profile; + } + + public List getProfileApps(Connection conn, String profileId) throws SQLException { + PreparedStatement pstmt = null; + ResultSet rs = null; + List apps = new ArrayList<>(); + try { + pstmt = conn.prepareStatement("" + + "select " + + " app.package_name " + + "from public.tms_device_profile_app app " + + "where app.profile_id=?::uuid"); + pstmt.setString(1, profileId); + rs = pstmt.executeQuery(); + while (rs.next()) { + String packageName = rs.getString(1); + apps.add(packageName); + } + } finally { + DBUtil.close(null, pstmt, rs); + } + return apps; + } + + public List getTerminalGroupIds(Connection conn, String terminalSN) throws SQLException { + PreparedStatement pstmt = null; + ResultSet rs = null; + List apps = new ArrayList<>(); + try { + pstmt = conn.prepareStatement("" + + "select ttg.id group_id " + + "from tms_terminal_group ttg " + + " inner join tms_terminal_group_link ttgl on ttgl.terminal_group_id = ttg.id " + + " inner join tms_terminal tt on ttgl.terminal_id = tt.id and tt.delete_ts is null " + + "where lower(tt.sn) = ?"); + pstmt.setString(1, terminalSN.toLowerCase()); + rs = pstmt.executeQuery(); + while (rs.next()) { + String groupId = rs.getString(1); + apps.add(groupId); + } + } finally { + DBUtil.close(null, pstmt, rs); + } + return apps; + } + +} diff --git a/src/id/iptek/utms/agent/dao/TerminalDao.java b/src/id/iptek/utms/agent/dao/TerminalDao.java new file mode 100644 index 0000000..0d7320e --- /dev/null +++ b/src/id/iptek/utms/agent/dao/TerminalDao.java @@ -0,0 +1,74 @@ +package id.iptek.utms.agent.dao; + +import id.iptek.utms.agent.db.DBConn; +import id.iptek.utms.agent.db.DBUtil; +import id.iptek.utms.agent.db.DatabaseException; +import id.iptek.utms.agent.model.TerminalIdObj; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Jaka + */ +public class TerminalDao { + + public TerminalDao() { + } + + public List getTerminalIds() throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList<>(); + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select id, sn " + + "from tms_terminal " + + "where delete_ts is null"); + rs = pstmt.executeQuery(); + while (rs.next()) { + String terminalId = rs.getString(1); + String sn = rs.getString(2); + list.add(new TerminalIdObj(sn, terminalId)); + } + } catch(SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return list; + } + + public TerminalIdObj getTerminalIdBySN(String sn) throws DatabaseException { + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + TerminalIdObj terminalIdObj = null; + try { + conn = DBConn.getInstance().getConnection(); + pstmt = conn.prepareStatement("" + + "select id, sn " + + "from tms_terminal " + + "where lower(sn)=? and delete_ts is null"); + pstmt.setString(1, sn.toLowerCase()); + rs = pstmt.executeQuery(); + if (rs.next()) { + String terminalId = rs.getString(1); + String _sn = rs.getString(2); + terminalIdObj = new TerminalIdObj(_sn, terminalId); + } + } catch(SQLException ex) { + throw new DatabaseException(ex); + } finally { + DBUtil.close(conn, pstmt, rs); + } + return terminalIdObj; + } + +} diff --git a/src/id/iptek/utms/agent/db/DBConn.java b/src/id/iptek/utms/agent/db/DBConn.java new file mode 100644 index 0000000..6a09e21 --- /dev/null +++ b/src/id/iptek/utms/agent/db/DBConn.java @@ -0,0 +1,69 @@ +package id.iptek.utms.agent.db; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jaka Ramdani + */ +public class DBConn { + + private static Logger logger = LoggerFactory.getLogger(DBConn.class); + + private boolean isUseDatasource; + private String datasource; + private String driver; + private String url; + private String username; + private String password; + private static DBConn self; + + private DBConn() { + } + + public void init(String datasource) throws Exception { + this.datasource = datasource; + logger.debug("Using Data Source: {}", this.datasource); + this.isUseDatasource = true; + } + + public void init(String driver, String url, String username, String password) throws Exception { + this.driver = driver; + this.url = url; + this.username = username; + this.password = password; + + //load driver + logger.debug("Load JDBC driver: {}", this.driver); + Class.forName(this.driver); + } + + public Connection getConnection() throws SQLException { + if(!isUseDatasource) { + return DriverManager.getConnection(url, username, password); + } else { + try { + InitialContext initialContext = new InitialContext(); + DataSource dataSource = (DataSource) initialContext.lookup(this.datasource); + return dataSource.getConnection(); + } catch(NamingException ex) { + throw new SQLException("Naming Exception when getting connection from datasource: {}", this.datasource, ex); + } + } + } + + public static DBConn getInstance() { + if (self == null) { + self = new DBConn(); + } + return self; + } +} diff --git a/src/id/iptek/utms/agent/db/DBUtil.java b/src/id/iptek/utms/agent/db/DBUtil.java new file mode 100644 index 0000000..c3d77e5 --- /dev/null +++ b/src/id/iptek/utms/agent/db/DBUtil.java @@ -0,0 +1,96 @@ +package id.iptek.utms.agent.db; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class DBUtil { + + public DBUtil() { + } + + public static void rollback(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + } + + public static void close(Connection conn, Statement st) { + close(st); + close(conn); + } + + public static void close(Connection conn, PreparedStatement pstmt) { + close(pstmt); + close(conn); + } + + public static void close(Connection conn, Statement st, ResultSet rs) { + close(rs); + close(st); + close(conn); + } + + public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) { + close(rs); + close(pstmt); + close(conn); + } + + public static void close(Connection conn, CallableStatement cstmt, ResultSet rs) { + close(rs); + close(cstmt); + close(conn); + } + + public static void close(Connection conn) { + try { + if (conn != null) { + conn.close(); + conn = null; + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void close(PreparedStatement pstmt) { + try { + if (pstmt != null) { + pstmt.close(); + pstmt = null; + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void close(Statement st) { + try { + if (st != null) { + st.close(); + st = null; + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + public static void close(ResultSet rs) { + try { + if (rs != null) { + rs.close(); + rs = null; + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/id/iptek/utms/agent/db/DatabaseException.java b/src/id/iptek/utms/agent/db/DatabaseException.java new file mode 100644 index 0000000..f03eeb3 --- /dev/null +++ b/src/id/iptek/utms/agent/db/DatabaseException.java @@ -0,0 +1,16 @@ +package id.iptek.utms.agent.db; + +public class DatabaseException extends RuntimeException { + + public DatabaseException(String message) { + super(message); + } + + public DatabaseException(Throwable th) { + super(th); + } + + public DatabaseException(String message, Throwable th) { + super(message, th); + } +} diff --git a/src/id/iptek/utms/agent/model/Application.java b/src/id/iptek/utms/agent/model/Application.java new file mode 100644 index 0000000..011f9de --- /dev/null +++ b/src/id/iptek/utms/agent/model/Application.java @@ -0,0 +1,210 @@ +package id.iptek.utms.agent.model; + +import java.util.Date; + +public class Application { + + protected String id; + protected String appName; + protected String packageName; + protected String version; + protected String companyName; + protected boolean uninstallable; + protected String description; + protected String apkId; + protected String fileId; + protected String fileExt; + protected Date fileCreateDate; + protected String checksum; + + public Application() { + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the appName + */ + public String getAppName() { + return appName; + } + + /** + * @param appName the appName to set + */ + public void setAppName(String appName) { + this.appName = appName; + } + + /** + * @return the packageName + */ + public String getPackageName() { + return packageName; + } + + /** + * @param packageName the packageName to set + */ + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + /** + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * @return the companyName + */ + public String getCompanyName() { + return companyName; + } + + /** + * @param companyName the companyName to set + */ + public void setCompanyName(String companyName) { + this.companyName = companyName; + } + + /** + * @return the uninstallable + */ + public boolean isUninstallable() { + return uninstallable; + } + + /** + * @param uninstallable the uninstallable to set + */ + public void setUninstallable(boolean uninstallable) { + this.uninstallable = uninstallable; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the apkId + */ + public String getApkId() { + return apkId; + } + + /** + * @param apkId the apkId to set + */ + public void setApkId(String apkId) { + this.apkId = apkId; + } + + /** + * @return the fileId + */ + public String getFileId() { + return fileId; + } + + /** + * @param fileId the fileId to set + */ + public void setFileId(String fileId) { + this.fileId = fileId; + } + + /** + * @return the fileExt + */ + public String getFileExt() { + return fileExt; + } + + /** + * @param fileExt the fileExt to set + */ + public void setFileExt(String fileExt) { + this.fileExt = fileExt; + } + + /** + * @return the fileCreateDate + */ + public Date getFileCreateDate() { + return fileCreateDate; + } + + /** + * @param fileCreateDate the fileCreateDate to set + */ + public void setFileCreateDate(Date fileCreateDate) { + this.fileCreateDate = fileCreateDate; + } + + /** + * @return the checksum + */ + public String getChecksum() { + return checksum; + } + + /** + * @param checksum the checksum to set + */ + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Application{"); + sb.append("id=").append(id); + sb.append(", appName=").append(appName); + sb.append(", packageName=").append(packageName); + sb.append(", version=").append(version); + sb.append(", companyName=").append(companyName); + sb.append(", uninstallable=").append(uninstallable); + sb.append(", description=").append(description); + sb.append(", apkId=").append(apkId); + sb.append(", fileId=").append(fileId); + sb.append(", fileExt=").append(fileExt); + sb.append(", fileCreateDate=").append(fileCreateDate); + sb.append(", checksum=").append(checksum); + sb.append('}'); + return sb.toString(); + } +} diff --git a/src/id/iptek/utms/agent/model/ApplicationExt.java b/src/id/iptek/utms/agent/model/ApplicationExt.java new file mode 100644 index 0000000..f98afcd --- /dev/null +++ b/src/id/iptek/utms/agent/model/ApplicationExt.java @@ -0,0 +1,146 @@ +package id.iptek.utms.agent.model; + +import java.util.Date; + +/** + * + * @author jakar + */ +public class ApplicationExt extends Application { + + private String uniqueName; + private String downloadUrl; + private Date downloadUrlExp; + private long fileSize; + private String uniqueIconName; + private String iconUrl; + private Date iconUrlExp; + + /** + * @return the uniqueName + */ + public String getUniqueName() { + return uniqueName; + } + + /** + * @param uniqueName the uniqueName to set + */ + public void setUniqueName(String uniqueName) { + this.uniqueName = uniqueName; + } + + /** + * @return the downloadUrl + */ + public String getDownloadUrl() { + return downloadUrl; + } + + /** + * @param downloadUrl the downloadUrl to set + */ + public void setDownloadUrl(String downloadUrl) { + this.downloadUrl = downloadUrl; + } + + /** + * @return the downloadUrlExp + */ + public Date getDownloadUrlExp() { + return downloadUrlExp; + } + + /** + * @param downloadUrlExp the downloadUrlExp to set + */ + public void setDownloadUrlExp(Date downloadUrlExp) { + this.downloadUrlExp = downloadUrlExp; + } + + /** + * @return the fileSize + */ + public long getFileSize() { + return fileSize; + } + + /** + * @param fileSize the fileSize to set + */ + public void setFileSize(long fileSize) { + this.fileSize = fileSize; + } + + /** + * @return the uniqueIconName + */ + public String getUniqueIconName() { + return uniqueIconName; + } + + /** + * @param uniqueIconName the uniqueIconName to set + */ + public void setUniqueIconName(String uniqueIconName) { + this.uniqueIconName = uniqueIconName; + } + + /** + * @return the iconUrl + */ + public String getIconUrl() { + return iconUrl; + } + + /** + * @param iconUrl the iconUrl to set + */ + public void setIconUrl(String iconUrl) { + this.iconUrl = iconUrl; + } + + /** + * @return the iconUrlExp + */ + public Date getIconUrlExp() { + return iconUrlExp; + } + + /** + * @param iconUrlExp the iconUrlExp to set + */ + public void setIconUrlExp(Date iconUrlExp) { + this.iconUrlExp = iconUrlExp; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ApplicationExt{"); + sb.append("id=").append(id); + sb.append(", appName=").append(appName); + sb.append(", packageName=").append(packageName); + sb.append(", version=").append(version); + sb.append(", companyName=").append(companyName); + sb.append(", uninstallable=").append(uninstallable); + sb.append(", description=").append(description); + sb.append(", apkId=").append(apkId); + sb.append(", fileId=").append(fileId); + sb.append(", fileExt=").append(fileExt); + sb.append(", fileCreateDate=").append(fileCreateDate); + sb.append(", checksum=").append(checksum); + sb.append(", uniqueName=").append(uniqueName); + sb.append(", downloadUrl=").append(downloadUrl); + sb.append(", downloadUrlExp=").append(downloadUrlExp); + sb.append(", fileSize=").append(fileSize); + sb.append(", uniqueIconName=").append(uniqueIconName); + sb.append(", iconUrl=").append(iconUrl); + sb.append(", iconUrlExp=").append(iconUrlExp); + sb.append('}'); + return sb.toString(); + } + + + +} diff --git a/src/id/iptek/utms/agent/model/ApplicationSimple.java b/src/id/iptek/utms/agent/model/ApplicationSimple.java new file mode 100644 index 0000000..8207f0c --- /dev/null +++ b/src/id/iptek/utms/agent/model/ApplicationSimple.java @@ -0,0 +1,53 @@ +package id.iptek.utms.agent.model; + +import javax.annotation.Generated; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +@Generated("jsonschema2pojo") +public class ApplicationSimple { + + private String id; + @SerializedName("app_name") + @Expose + private String appName; + @SerializedName("package_name") + @Expose + private String packageName; + @SerializedName("version") + @Expose + private String version; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + +} diff --git a/src/id/iptek/utms/agent/model/CellInfo.java b/src/id/iptek/utms/agent/model/CellInfo.java new file mode 100644 index 0000000..b47e3b8 --- /dev/null +++ b/src/id/iptek/utms/agent/model/CellInfo.java @@ -0,0 +1,55 @@ +package id.iptek.utms.agent.model; + +import javax.annotation.Generated; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +@Generated("jsonschema2pojo") +public class CellInfo { + + @SerializedName("name") + @Expose + private String name; + @SerializedName("cid") + @Expose + private String cid; + @SerializedName("type") + @Expose + private String type; + @SerializedName("strength") + @Expose + private Integer strength; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCid() { + return cid; + } + + public void setCid(String cid) { + this.cid = cid; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Integer getStrength() { + return strength; + } + + public void setStrength(Integer strength) { + this.strength = strength; + } + +} diff --git a/src/id/iptek/utms/agent/model/Diagnostic.java b/src/id/iptek/utms/agent/model/Diagnostic.java new file mode 100644 index 0000000..31f5d8d --- /dev/null +++ b/src/id/iptek/utms/agent/model/Diagnostic.java @@ -0,0 +1,140 @@ +package id.iptek.utms.agent.model; + +import java.util.List; +import javax.annotation.Generated; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import java.util.Date; + +@Generated("jsonschema2pojo") +public class Diagnostic { + + private String id; + @SerializedName("req_id") + @Expose + private String reqId; + @SerializedName("req_type") + @Expose + private String reqType; + @SerializedName("req_time") + @Expose + private String reqTime; + @SerializedName("device_sn") + @Expose + private String deviceSn; + @SerializedName("diagnostic_info") + @Expose + private DiagnosticInfo diagnosticInfo; + @SerializedName("location_info") + @Expose + private LocationInfo locationInfo; + @SerializedName("cell_info") + @Expose + private List cellInfo; + @SerializedName("installed_apps") + @Expose + private List installedApps = null; + private Date createTime; + private String terminalId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getReqId() { + return reqId; + } + + public void setReqId(String reqId) { + this.reqId = reqId; + } + + public String getReqType() { + return reqType; + } + + public void setReqType(String reqType) { + this.reqType = reqType; + } + + public String getReqTime() { + return reqTime; + } + + public void setReqTime(String reqTime) { + this.reqTime = reqTime; + } + + public String getDeviceSn() { + return deviceSn; + } + + public void setDeviceSn(String deviceSn) { + this.deviceSn = deviceSn; + } + + public DiagnosticInfo getDiagnosticInfo() { + return diagnosticInfo; + } + + public void setDiagnosticInfo(DiagnosticInfo diagnosticInfo) { + this.diagnosticInfo = diagnosticInfo; + } + + public LocationInfo getLocationInfo() { + return locationInfo; + } + + public void setLocationInfo(LocationInfo locationInfo) { + this.locationInfo = locationInfo; + } + + public List getCellInfo() { + return cellInfo; + } + + public void setCellInfo(List cellInfo) { + this.cellInfo = cellInfo; + } + + public List getInstalledApps() { + return installedApps; + } + + public void setInstalledApps(List installedApps) { + this.installedApps = installedApps; + } + + /** + * @return the createTime + */ + public Date getCreateTime() { + return createTime; + } + + /** + * @param createTime the createTime to set + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * @return the terminalId + */ + public String getTerminalId() { + return terminalId; + } + + /** + * @param terminalId the terminalId to set + */ + public void setTerminalId(String terminalId) { + this.terminalId = terminalId; + } + +} diff --git a/src/id/iptek/utms/agent/model/DiagnosticInfo.java b/src/id/iptek/utms/agent/model/DiagnosticInfo.java new file mode 100644 index 0000000..67b8243 --- /dev/null +++ b/src/id/iptek/utms/agent/model/DiagnosticInfo.java @@ -0,0 +1,246 @@ +package id.iptek.utms.agent.model; + +import javax.annotation.Generated; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +@Generated("jsonschema2pojo") +public class DiagnosticInfo { + + @SerializedName("imei") + @Expose + private String imei; + @SerializedName("meid") + @Expose + private String meid; + @SerializedName("battery_temp") + @Expose + private double batteryTemp; + @SerializedName("battery_percentage") + @Expose + private double batteryPercentage; + @SerializedName("total_memory") + @Expose + private String totalMemory; + @SerializedName("available_memory") + @Expose + private String availableMemory; + @SerializedName("total_flash_memory") + @Expose + private String totalFlashMemory; + @SerializedName("available_flash_memory") + @Expose + private String availableFlashMemory; + @SerializedName("total_mobile_data") + @Expose + private String totalMobileData; + @SerializedName("switching_times") + @Expose + private int switchingTimes; + @SerializedName("current_boot_time") + @Expose + private int currentBootTime; + @SerializedName("total_boot_time") + @Expose + private int totalBootTime; + @SerializedName("total_length_printed") + @Expose + private double totalLengthPrinted; + @SerializedName("swiping_card_times") + @Expose + private int swipingCardTimes; + @SerializedName("dip_inserting_times") + @Expose + private int dipInsertingTimes; + @SerializedName("nfc_card_reading_times") + @Expose + private int nfcCardReadingTimes; + @SerializedName("front_camera_open_times") + @Expose + private int frontCameraOpenTimes; + @SerializedName("rear_camera_open_times") + @Expose + private int rearCameraOpenTimes; + @SerializedName("charge_times") + @Expose + private int chargeTimes; + @SerializedName("new_diagnostic") + @Expose + private Boolean newDiagnostic; + private String installedAppString; + + public String getImei() { + return imei; + } + + public void setImei(String imei) { + this.imei = imei; + } + + public String getMeid() { + return meid; + } + + public void setMeid(String meid) { + this.meid = meid; + } + + public double getBatteryTemp() { + return batteryTemp; + } + + public void setBatteryTemp(double batteryTemp) { + this.batteryTemp = batteryTemp; + } + + public double getBatteryPercentage() { + return batteryPercentage; + } + + public void setBatteryPercentage(double batteryPercentage) { + this.batteryPercentage = batteryPercentage; + } + + public String getTotalMemory() { + return totalMemory; + } + + public void setTotalMemory(String totalMemory) { + this.totalMemory = totalMemory; + } + + public String getAvailableMemory() { + return availableMemory; + } + + public void setAvailableMemory(String availableMemory) { + this.availableMemory = availableMemory; + } + + public String getTotalFlashMemory() { + return totalFlashMemory; + } + + public void setTotalFlashMemory(String totalFlashMemory) { + this.totalFlashMemory = totalFlashMemory; + } + + public String getAvailableFlashMemory() { + return availableFlashMemory; + } + + public void setAvailableFlashMemory(String availableFlashMemory) { + this.availableFlashMemory = availableFlashMemory; + } + + public String getTotalMobileData() { + return totalMobileData; + } + + public void setTotalMobileData(String totalMobileData) { + this.totalMobileData = totalMobileData; + } + + public int getSwitchingTimes() { + return switchingTimes; + } + + public void setSwitchingTimes(int switchingTimes) { + this.switchingTimes = switchingTimes; + } + + public int getCurrentBootTime() { + return currentBootTime; + } + + public void setCurrentBootTime(int currentBootTime) { + this.currentBootTime = currentBootTime; + } + + public int getTotalBootTime() { + return totalBootTime; + } + + public void setTotalBootTime(int totalBootTime) { + this.totalBootTime = totalBootTime; + } + + public double getTotalLengthPrinted() { + return totalLengthPrinted; + } + + public void setTotalLengthPrinted(double totalLengthPrinted) { + this.totalLengthPrinted = totalLengthPrinted; + } + + public int getSwipingCardTimes() { + return swipingCardTimes; + } + + public void setSwipingCardTimes(int swipingCardTimes) { + this.swipingCardTimes = swipingCardTimes; + } + + public int getDipInsertingTimes() { + return dipInsertingTimes; + } + + public void setDipInsertingTimes(int dipInsertingTimes) { + this.dipInsertingTimes = dipInsertingTimes; + } + + public int getNfcCardReadingTimes() { + return nfcCardReadingTimes; + } + + public void setNfcCardReadingTimes(int nfcCardReadingTimes) { + this.nfcCardReadingTimes = nfcCardReadingTimes; + } + + public int getFrontCameraOpenTimes() { + return frontCameraOpenTimes; + } + + public void setFrontCameraOpenTimes(int frontCameraOpenTimes) { + this.frontCameraOpenTimes = frontCameraOpenTimes; + } + + public int getRearCameraOpenTimes() { + return rearCameraOpenTimes; + } + + public void setRearCameraOpenTimes(int rearCameraOpenTimes) { + this.rearCameraOpenTimes = rearCameraOpenTimes; + } + + public int getChargeTimes() { + return chargeTimes; + } + + public void setChargeTimes(int chargeTimes) { + this.chargeTimes = chargeTimes; + } + + public Boolean getNewDiagnostic() { + return newDiagnostic; + } + + public void setNewDiagnostic(Boolean newDiagnostic) { + this.newDiagnostic = newDiagnostic; + } + + /** + * @return the installedAppString + */ + public String getInstalledAppString() { + return installedAppString; + } + + /** + * @param installedAppString the installedAppString to set + */ + public void setInstalledAppString(String installedAppString) { + this.installedAppString = installedAppString; + } + +} diff --git a/src/id/iptek/utms/agent/model/DownloadTask.java b/src/id/iptek/utms/agent/model/DownloadTask.java new file mode 100644 index 0000000..34982b3 --- /dev/null +++ b/src/id/iptek/utms/agent/model/DownloadTask.java @@ -0,0 +1,176 @@ +package id.iptek.utms.agent.model; + +import id.iptek.utms.agent.model.enumeration.DownloadTimeType; +import id.iptek.utms.agent.model.enumeration.InstallationNotificationType; +import id.iptek.utms.agent.model.enumeration.InstallationTimeType; +import id.iptek.utms.agent.model.enumeration.PublishTimeType; +import id.iptek.utms.agent.model.enumeration.TaskStatus; +import java.io.Serializable; +import java.util.Date; + +/** + * + * @author Jaka + */ +public class DownloadTask implements Serializable { + + private String id; + private String name; + private PublishTimeType publishTimeType; + private Date publishTime; + private DownloadTimeType downloadTimeType; + private Date downloadTime; + private InstallationTimeType installationTimeType; + private Date installationTime; + private InstallationNotificationType installationNotification; + private TaskStatus status; + + public DownloadTask() { + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the publishTimeType + */ + public PublishTimeType getPublishTimeType() { + return publishTimeType; + } + + /** + * @param publishTimeType the publishTimeType to set + */ + public void setPublishTimeType(int publishTimeType) { + this.publishTimeType = PublishTimeType.fromValue(publishTimeType); + } + + /** + * @return the publishTime + */ + public Date getPublishTime() { + return publishTime; + } + + /** + * @param publishTime the publishTime to set + */ + public void setPublishTime(Date publishTime) { + this.publishTime = publishTime; + } + + /** + * @return the downloadTimeType + */ + public DownloadTimeType getDownloadTimeType() { + return downloadTimeType; + } + + /** + * @param downloadTimeType the downloadTimeType to set + */ + public void setDownloadTimeType(int downloadTimeType) { + this.downloadTimeType = DownloadTimeType.fromValue(downloadTimeType); + } + + /** + * @return the downloadTime + */ + public Date getDownloadTime() { + return downloadTime; + } + + /** + * @param downloadTime the downloadTime to set + */ + public void setDownloadTime(Date downloadTime) { + this.downloadTime = downloadTime; + } + + /** + * @return the installationTimeType + */ + public InstallationTimeType getInstallationTimeType() { + return installationTimeType; + } + + /** + * @param installationTimeType the installationTimeType to set + */ + public void setInstallationTimeType(int installationTimeType) { + this.installationTimeType = InstallationTimeType.fromValue(installationTimeType); + } + + /** + * @return the installationTime + */ + public Date getInstallationTime() { + return installationTime; + } + + /** + * @param installationTime the installationTime to set + */ + public void setInstallationTime(Date installationTime) { + this.installationTime = installationTime; + } + + /** + * @return the installationNotification + */ + public InstallationNotificationType getInstallationNotification() { + return installationNotification; + } + + /** + * @param installationNotification the installationNotification to set + */ + public void setInstallationNotification(int installationNotification) { + this.installationNotification = InstallationNotificationType.fromValue(installationNotification); + } + + /** + * @return the status + */ + public TaskStatus getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(int status) { + this.status = TaskStatus.fromValue(status); + } + + @Override + public String toString() { + return "DownloadTask{" + "id=" + id + ", name=" + name + ", publishTimeType=" + publishTimeType + ", publishTime=" + publishTime + ", downloadTimeType=" + downloadTimeType + ", downloadTime=" + downloadTime + ", installationTimeType=" + installationTimeType + ", installationTime=" + installationTime + ", installationNotification=" + installationNotification + ", status=" + status + '}'; + } + +} diff --git a/src/id/iptek/utms/agent/model/FileReaderCacheObj.java b/src/id/iptek/utms/agent/model/FileReaderCacheObj.java new file mode 100644 index 0000000..3593b6f --- /dev/null +++ b/src/id/iptek/utms/agent/model/FileReaderCacheObj.java @@ -0,0 +1,75 @@ +package id.iptek.utms.agent.model; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * @author jakar + */ +public class FileReaderCacheObj implements Serializable { + + private File file; + private BufferedReader reader; + private final AtomicInteger currentReadRowNum = new AtomicInteger(0); + + public FileReaderCacheObj() { + } + + /** + * @return the file + */ + public File getFile() { + return file; + } + + /** + * @param file the file to set + */ + public void setFile(File file) { + this.file = file; + } + + /** + * @return the reader + */ + public BufferedReader getReader() { + return reader; + } + + /** + * @return the currentReadRowNum + */ + public AtomicInteger getCurrentReadRowNum() { + return currentReadRowNum; + } + + public void pointToFile(File file) throws IOException { + if(file == null) { + throw new IllegalArgumentException("File cannot be null!"); + } + if(!file.exists()) { + throw new IOException("File not exists!"); + } + if(!file.canRead()) { + throw new IOException("File not readable!"); + } + this.file = file; + this.reader = new BufferedReader(new FileReader(this.file)); + this.currentReadRowNum.set(0); + } + + public void closeReader() throws IOException { + if(this.reader != null) { + this.reader.close(); + this.file = null; + this.currentReadRowNum.set(0); + } else { + // ignore null reader + } + } +} diff --git a/src/id/iptek/utms/agent/model/FileWriterCacheObj.java b/src/id/iptek/utms/agent/model/FileWriterCacheObj.java new file mode 100644 index 0000000..1f40d54 --- /dev/null +++ b/src/id/iptek/utms/agent/model/FileWriterCacheObj.java @@ -0,0 +1,118 @@ +package id.iptek.utms.agent.model; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * @author jakar + */ +public class FileWriterCacheObj implements Serializable { + + private File file; + private BufferedWriter writer; + private final AtomicInteger currentWriteRowNum = new AtomicInteger(0); + private final Object lock = new Object(); + + public FileWriterCacheObj() { + } + + /** + * @return the file + */ + public File getFile() { + return file; + } + + /** + * @param file the file to set + */ + public void setFile(File file) { + this.file = file; + } + + /** + * @return the reader + */ + public BufferedWriter getWriter() { + return writer; + } + + /** + * @return the currentReadRowNum + */ + public AtomicInteger getCurrentWriteRowNum() { + return currentWriteRowNum; + } + + public void pointToFile(File file) throws IOException { + if(file == null) { + throw new IllegalArgumentException("File cannot be null!"); + } + /* + if(file.exists()) { + throw new IOException("File exists!"); + } + if(!file.canWrite()) { + throw new IOException("File not writable!"); + }*/ + // close old writer + synchronized (lock) { + if(this.writer != null) { + this.writer.flush(); + try { + this.writer.close(); + } catch(IOException ignore) {} + this.writer = null; + } + this.file = file; + this.writer = new BufferedWriter(new FileWriter(this.file)); + this.currentWriteRowNum.set(0); + } + } + + public void closeWriter() throws IOException { + if(this.writer != null) { + synchronized (lock) { + this.writer.close(); + this.file = null; + this.currentWriteRowNum.set(0); + } + } else { + // ignore null reader + } + } + + public BufferedWriter writeLine(String line) throws IOException { + if(this.writer == null) { + throw new IOException("Writer cannot be null!"); + } + synchronized (lock) { + this.writer.write(line); + this.writer.newLine(); + this.writer.flush(); + } + this.currentWriteRowNum.incrementAndGet(); + return this.writer; + } + + public boolean writeLineWithMaxLineNo(int maxLineNo, String line) throws IOException { + if(this.writer == null) { + throw new IOException("Writer cannot be null!"); + } + if(this.currentWriteRowNum.get() < maxLineNo) { + synchronized (lock) { + this.writer.write(line); + this.writer.newLine(); + this.writer.flush(); + } + this.currentWriteRowNum.incrementAndGet(); + return true; + } + return false; + } +} diff --git a/src/id/iptek/utms/agent/model/HeartBeat.java b/src/id/iptek/utms/agent/model/HeartBeat.java new file mode 100644 index 0000000..3a980d1 --- /dev/null +++ b/src/id/iptek/utms/agent/model/HeartBeat.java @@ -0,0 +1,200 @@ +package id.iptek.utms.agent.model; + +import javax.annotation.Generated; +import com.google.gson.annotations.Expose; + +import com.google.gson.annotations.SerializedName; +import java.io.File; +import java.util.Date; +import java.util.List; + +@Generated("jsonschema2pojo") +public class HeartBeat { + + private String id; + @SerializedName("req_id") + @Expose + private String reqId; + @SerializedName("req_type") + @Expose + private String reqType; + @SerializedName("req_time") + @Expose + private String reqTime; + @SerializedName("device_sn") + @Expose + private String deviceSn; + @SerializedName("diagnostic_info") + @Expose + private DiagnosticInfo diagnosticInfo; + @SerializedName("location_info") + @Expose + private LocationInfo locationInfo; + @SerializedName("cell_info") + @Expose + private List cellInfo; + private File fileLocation; + private Date createTime; + private String terminalId; + private String cellInfoString; + @Expose + @SerializedName("cell_type") + private String cellType; + @Expose + @SerializedName("cell_name") + private String cellName; + @Expose + @SerializedName("cell_strength") + private Integer cellStrength; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getReqId() { + return reqId; + } + + public void setReqId(String reqId) { + this.reqId = reqId; + } + + public String getReqType() { + return reqType; + } + + public void setReqType(String reqType) { + this.reqType = reqType; + } + + public String getReqTime() { + return reqTime; + } + + public void setReqTime(String reqTime) { + this.reqTime = reqTime; + } + + public String getDeviceSn() { + return deviceSn; + } + + public void setDeviceSn(String deviceSn) { + this.deviceSn = deviceSn; + } + + public DiagnosticInfo getDiagnosticInfo() { + return diagnosticInfo; + } + + public void setDiagnosticInfo(DiagnosticInfo diagnosticInfo) { + this.diagnosticInfo = diagnosticInfo; + } + + public LocationInfo getLocationInfo() { + return locationInfo; + } + + public void setLocationInfo(LocationInfo locationInfo) { + this.locationInfo = locationInfo; + } + + public List getCellInfo() { + return cellInfo; + } + + public void setCellInfo(List cellInfo) { + this.cellInfo = cellInfo; + } + + public File getFileLocation() { + return fileLocation; + } + + public void setFileLocation(File fileLocation) { + this.fileLocation = fileLocation; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * @return the terminalId + */ + public String getTerminalId() { + return terminalId; + } + + /** + * @param terminalId the terminalId to set + */ + public void setTerminalId(String terminalId) { + this.terminalId = terminalId; + } + + /** + * @return the cellInfoString + */ + public String getCellInfoString() { + return cellInfoString; + } + + /** + * @param cellInfoString the cellInfoString to set + */ + public void setCellInfoString(String cellInfoString) { + this.cellInfoString = cellInfoString; + } + + /** + * @return the cellType + */ + public String getCellType() { + return cellType; + } + + /** + * @param cellType the cellType to set + */ + public void setCellType(String cellType) { + this.cellType = cellType; + } + + /** + * @return the cellName + */ + public String getCellName() { + return cellName; + } + + /** + * @param cellName the cellName to set + */ + public void setCellName(String cellName) { + this.cellName = cellName; + } + + /** + * @return the cellStrength + */ + public Integer getCellStrength() { + return cellStrength; + } + + /** + * @param cellStrength the cellStrength to set + */ + public void setCellStrength(Integer cellStrength) { + this.cellStrength = cellStrength; + } + +} diff --git a/src/id/iptek/utms/agent/model/LocationInfo.java b/src/id/iptek/utms/agent/model/LocationInfo.java new file mode 100644 index 0000000..d0b1e42 --- /dev/null +++ b/src/id/iptek/utms/agent/model/LocationInfo.java @@ -0,0 +1,34 @@ +package id.iptek.utms.agent.model; + +import javax.annotation.Generated; +import com.google.gson.annotations.Expose; + +import com.google.gson.annotations.SerializedName; + +@Generated("jsonschema2pojo") +public class LocationInfo { + + @SerializedName("lat") + @Expose + private Double lat; + @SerializedName("lng") + @Expose + private Double lng; + + public Double getLat() { + return lat; + } + + public void setLat(Double lat) { + this.lat = lat; + } + + public Double getLng() { + return lng; + } + + public void setLng(Double lng) { + this.lng = lng; + } + +} diff --git a/src/id/iptek/utms/agent/model/PendingDeleteTask.java b/src/id/iptek/utms/agent/model/PendingDeleteTask.java new file mode 100644 index 0000000..3ff0164 --- /dev/null +++ b/src/id/iptek/utms/agent/model/PendingDeleteTask.java @@ -0,0 +1,130 @@ +package id.iptek.utms.agent.model; + +import java.io.Serializable; +import java.util.Date; + +/** + * + * @author Jaka + */ +public class PendingDeleteTask implements Serializable { + + private String terminalSN; + private String id; + private String name; + private Date deleteTime; + private Date lastBroadcastTs; + private ApplicationSimple app; + private int activity; + private String logId; + + public PendingDeleteTask() { + } + + /** + * @return the terminalSN + */ + public String getTerminalSN() { + return terminalSN; + } + + /** + * @param terminalSN the terminalSN to set + */ + public void setTerminalSN(String terminalSN) { + this.terminalSN = terminalSN; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the deleteTime + */ + public Date getDeleteTime() { + return deleteTime; + } + + /** + * @param deleteTime the deleteTime to set + */ + public void setDeleteTime(Date deleteTime) { + this.deleteTime = deleteTime; + } + + /** + * @return the lastBroadcastTs + */ + public Date getLastBroadcastTs() { + return lastBroadcastTs; + } + + /** + * @param lastBroadcastTs the lastBroadcastTs to set + */ + public void setLastBroadcastTs(Date lastBroadcastTs) { + this.lastBroadcastTs = lastBroadcastTs; + } + + /** + * @return the app + */ + public ApplicationSimple getApp() { + return app; + } + + /** + * @param app the app to set + */ + public void setApp(ApplicationSimple app) { + this.app = app; + } + + /** + * @return the activity + */ + public int getActivity() { + return activity; + } + + /** + * @param activity the activity to set + */ + public void setActivity(int activity) { + this.activity = activity; + } + + public String getLogId() { + return logId; + } + + public void setLogId(String logId) { + this.logId = logId; + } + +} diff --git a/src/id/iptek/utms/agent/model/PendingDownloadTask.java b/src/id/iptek/utms/agent/model/PendingDownloadTask.java new file mode 100644 index 0000000..b081002 --- /dev/null +++ b/src/id/iptek/utms/agent/model/PendingDownloadTask.java @@ -0,0 +1,234 @@ +package id.iptek.utms.agent.model; + +import java.io.Serializable; +import java.util.Date; + +/** + * + * @author Jaka + */ +public class PendingDownloadTask implements Serializable { + + private String terminalSN; + private String terminalGroupId; + private String id; + private String name; + private int downloadTimeType; + private Date downloadTime; + private int installationTimeType; + private Date installationTime; + private int installationNotification; + private Date lastBroadcastTs; + private int activity; + private String logId; + private Application app; + + public PendingDownloadTask() { + } + + /** + * @return the terminalSN + */ + public String getTerminalSN() { + return terminalSN; + } + + /** + * @param terminalSN the terminalSN to set + */ + public void setTerminalSN(String terminalSN) { + this.terminalSN = terminalSN; + } + + + + /** + * @return the terminalGroupId + */ + public String getTerminalGroupId() { + return terminalGroupId; + } + + /** + * @param terminalGroupId the terminalGroupId to set + */ + public void setTerminalGroupId(String terminalGroupId) { + this.terminalGroupId = terminalGroupId; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the downloadTimeType + */ + public int getDownloadTimeType() { + return downloadTimeType; + } + + /** + * @param downloadTimeType the downloadTimeType to set + */ + public void setDownloadTimeType(int downloadTimeType) { + this.downloadTimeType = downloadTimeType; + } + + /** + * @return the downloadTime + */ + public Date getDownloadTime() { + return downloadTime; + } + + /** + * @param downloadTime the downloadTime to set + */ + public void setDownloadTime(Date downloadTime) { + this.downloadTime = downloadTime; + } + + /** + * @return the installationTimeType + */ + public int getInstallationTimeType() { + return installationTimeType; + } + + /** + * @param installationTimeType the installationTimeType to set + */ + public void setInstallationTimeType(int installationTimeType) { + this.installationTimeType = installationTimeType; + } + + /** + * @return the installationTime + */ + public Date getInstallationTime() { + return installationTime; + } + + /** + * @param installationTime the installationTime to set + */ + public void setInstallationTime(Date installationTime) { + this.installationTime = installationTime; + } + + /** + * @return the installationNotification + */ + public int getInstallationNotification() { + return installationNotification; + } + + /** + * @param installationNotification the installationNotification to set + */ + public void setInstallationNotification(int installationNotification) { + this.installationNotification = installationNotification; + } + + /** + * @return the lastBroadcastTs + */ + public Date getLastBroadcastTs() { + return lastBroadcastTs; + } + + /** + * @param lastBroadcastTs the lastBroadcastTs to set + */ + public void setLastBroadcastTs(Date lastBroadcastTs) { + this.lastBroadcastTs = lastBroadcastTs; + } + + /** + * @return the activity + */ + public int getActivity() { + return activity; + } + + /** + * @param activity the activity to set + */ + public void setActivity(int activity) { + this.activity = activity; + } + + /** + * @return the logId + */ + public String getLogId() { + return logId; + } + + /** + * @param logId the logId to set + */ + public void setLogId(String logId) { + this.logId = logId; + } + + /** + * @return the app + */ + public Application getApp() { + return app; + } + + /** + * @param app the app to set + */ + public void setApp(Application app) { + this.app = app; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("PendingDownloadTask{"); + sb.append("terminalSN=").append(terminalSN); + sb.append(", terminalGroupId=").append(terminalGroupId); + sb.append(", id=").append(id); + sb.append(", name=").append(name); + sb.append(", downloadTimeType=").append(downloadTimeType); + sb.append(", downloadTime=").append(downloadTime); + sb.append(", installationTimeType=").append(installationTimeType); + sb.append(", installationTime=").append(installationTime); + sb.append(", installationNotification=").append(installationNotification); + sb.append(", lastBroadcastTs=").append(lastBroadcastTs); + sb.append(", activity=").append(activity); + sb.append(", logId=").append(logId); + sb.append(", app=").append(app); + sb.append('}'); + return sb.toString(); + } + +} diff --git a/src/id/iptek/utms/agent/model/Profile.java b/src/id/iptek/utms/agent/model/Profile.java new file mode 100644 index 0000000..0a5f5ed --- /dev/null +++ b/src/id/iptek/utms/agent/model/Profile.java @@ -0,0 +1,257 @@ +package id.iptek.utms.agent.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * + * @author Jaka + */ +public class Profile implements Serializable { + + private String terminalSN; + private String id; + private String name; + private int heartbeatInterval; + private int diagnosticInterval; + private boolean maskHomeButton; + private boolean maskStatusButton; + private boolean scheduleReboot; + private Date scheduleRebootTime; + private boolean isDefault; + private boolean relocationAlert; + private int movingThreshold; + private String adminPassword; + private String frontApp; + private List groupIds; + private List apps; + + /** + * @return the terminalSN + */ + public String getTerminalSN() { + return terminalSN; + } + + /** + * @param terminalSN the terminalSN to set + */ + public void setTerminalSN(String terminalSN) { + this.terminalSN = terminalSN; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the heartbeatInterval + */ + public int getHeartbeatInterval() { + return heartbeatInterval; + } + + /** + * @param heartbeatInterval the heartbeatInterval to set + */ + public void setHeartbeatInterval(int heartbeatInterval) { + this.heartbeatInterval = heartbeatInterval; + } + + /** + * @return the diagnosticInterval + */ + public int getDiagnosticInterval() { + return diagnosticInterval; + } + + /** + * @param diagnosticInterval the diagnosticInterval to set + */ + public void setDiagnosticInterval(int diagnosticInterval) { + this.diagnosticInterval = diagnosticInterval; + } + + /** + * @return the maskHomeButton + */ + public boolean isMaskHomeButton() { + return maskHomeButton; + } + + /** + * @param maskHomeButton the maskHomeButton to set + */ + public void setMaskHomeButton(boolean maskHomeButton) { + this.maskHomeButton = maskHomeButton; + } + + /** + * @return the maskStatusButton + */ + public boolean isMaskStatusButton() { + return maskStatusButton; + } + + /** + * @param maskStatusButton the maskStatusButton to set + */ + public void setMaskStatusButton(boolean maskStatusButton) { + this.maskStatusButton = maskStatusButton; + } + + /** + * @return the scheduleReboot + */ + public boolean isScheduleReboot() { + return scheduleReboot; + } + + /** + * @param scheduleReboot the scheduleReboot to set + */ + public void setScheduleReboot(boolean scheduleReboot) { + this.scheduleReboot = scheduleReboot; + } + + /** + * @return the scheduleRebootTime + */ + public Date getScheduleRebootTime() { + return scheduleRebootTime; + } + + /** + * @param scheduleRebootTime the scheduleRebootTime to set + */ + public void setScheduleRebootTime(Date scheduleRebootTime) { + this.scheduleRebootTime = scheduleRebootTime; + } + + /** + * @return the isDefault + */ + public boolean isDefault() { + return isDefault; + } + + /** + * @param isDefault the isDefault to set + */ + public void setDefault(boolean isDefault) { + this.isDefault = isDefault; + } + + /** + * @return the relocationAlert + */ + public boolean isRelocationAlert() { + return relocationAlert; + } + + /** + * @param relocationAlert the relocationAlert to set + */ + public void setRelocationAlert(boolean relocationAlert) { + this.relocationAlert = relocationAlert; + } + + /** + * @return the movingThreshold + */ + public int getMovingThreshold() { + return movingThreshold; + } + + /** + * @param movingThreshold the movingThreshold to set + */ + public void setMovingThreshold(int movingThreshold) { + this.movingThreshold = movingThreshold; + } + + /** + * @return the adminPassword + */ + public String getAdminPassword() { + return adminPassword; + } + + /** + * @param adminPassword the adminPassword to set + */ + public void setAdminPassword(String adminPassword) { + this.adminPassword = adminPassword; + } + + /** + * @return the frontApp + */ + public String getFrontApp() { + return frontApp; + } + + /** + * @param frontApp the frontApp to set + */ + public void setFrontApp(String frontApp) { + this.frontApp = frontApp; + } + + /** + * @return the apps + */ + public List getApps() { + if(apps == null) { + apps = new ArrayList<>(); + } + return apps; + } + + /** + * @param apps the apps to set + */ + public void setApps(List apps) { + this.apps = apps; + } + + /** + * @return the groupIds + */ + public List getGroupIds() { + return groupIds; + } + + /** + * @param groupIds the groupIds to set + */ + public void setGroupIds(List groupIds) { + this.groupIds = groupIds; + } +} diff --git a/src/id/iptek/utms/agent/model/SimplePendingDownloadTask.java b/src/id/iptek/utms/agent/model/SimplePendingDownloadTask.java new file mode 100644 index 0000000..d6d7150 --- /dev/null +++ b/src/id/iptek/utms/agent/model/SimplePendingDownloadTask.java @@ -0,0 +1,61 @@ +package id.iptek.utms.agent.model; + +import java.io.Serializable; +import java.util.List; + +/** + * + * @author Jaka + */ +public class SimplePendingDownloadTask implements Serializable { + + private String id; + private String name; + private List groupIds; + + public SimplePendingDownloadTask() { + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the groupIds + */ + public List getGroupIds() { + return groupIds; + } + + /** + * @param groupIds the groupIds to set + */ + public void setGroupIds(List groupIds) { + this.groupIds = groupIds; + } + +} diff --git a/src/id/iptek/utms/agent/model/TaskAckReq.java b/src/id/iptek/utms/agent/model/TaskAckReq.java new file mode 100644 index 0000000..d0cfe6f --- /dev/null +++ b/src/id/iptek/utms/agent/model/TaskAckReq.java @@ -0,0 +1,120 @@ +package id.iptek.utms.agent.model; + +import java.io.Serializable; + +/** + * + * @author Jaka + */ +public class TaskAckReq implements Serializable { + + private String reqId; + private String terminalSN; + private String ackId; + private String terminalId; + private int activity; + private String message; + private String taskId; + private String terminalGroupId; + private boolean broadcasted; + + public TaskAckReq() { + } + + /** + * @return the reqId + */ + public String getReqId() { + return reqId; + } + + /** + * @param reqId the reqId to set + */ + public void setReqId(String reqId) { + this.reqId = reqId; + } + + /** + * @return the terminalSN + */ + public String getTerminalSN() { + return terminalSN; + } + + /** + * @param terminalSN the terminalSN to set + */ + public void setTerminalSN(String terminalSN) { + this.terminalSN = terminalSN; + } + + /** + * @return the ackId + */ + public String getAckId() { + return ackId; + } + + /** + * @param ackId the ackId to set + */ + public void setAckId(String ackId) { + this.ackId = ackId; + } + + public String getTerminalId() { + return terminalId; + } + + public void setTerminalId(String terminalId) { + this.terminalId = terminalId; + } + + public String getTerminalGroupId() { + return terminalGroupId; + } + + public void setTerminalGroupId(String terminalGroupId) { + this.terminalGroupId = terminalGroupId; + } + + public int getActivity() { + return activity; + } + + public void setActivity(int activity) { + this.activity = activity; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public boolean isBroadcasted() { + return broadcasted; + } + + public void setBroadcasted(boolean broadcasted) { + this.broadcasted = broadcasted; + } + +} diff --git a/src/id/iptek/utms/agent/model/TerminalIdObj.java b/src/id/iptek/utms/agent/model/TerminalIdObj.java new file mode 100644 index 0000000..b7a97bb --- /dev/null +++ b/src/id/iptek/utms/agent/model/TerminalIdObj.java @@ -0,0 +1,80 @@ +package id.iptek.utms.agent.model; + +import java.io.Serializable; +import java.util.Objects; + +/** + * + * @author jakar + */ +public class TerminalIdObj implements Serializable { + + private String sn; + private String terminalId; + + public TerminalIdObj() { + } + + public TerminalIdObj(String sn, String terminalId) { + this.sn = sn; + this.terminalId = terminalId; + } + + /** + * @return the sn + */ + public String getSn() { + return sn; + } + + /** + * @param sn the sn to set + */ + public void setSn(String sn) { + this.sn = sn; + } + + /** + * @return the terminalId + */ + public String getTerminalId() { + return terminalId; + } + + /** + * @param terminalId the terminalId to set + */ + public void setTerminalId(String terminalId) { + this.terminalId = terminalId; + } + + @Override + public int hashCode() { + int hash = 3; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TerminalIdObj other = (TerminalIdObj) obj; + if (!Objects.equals(this.sn, other.sn)) { + return false; + } + return Objects.equals(this.terminalId, other.terminalId); + } + + @Override + public String toString() { + return "TerminalIdObj{" + "sn=" + sn + ", terminalId=" + terminalId + '}'; + } + +} diff --git a/src/id/iptek/utms/agent/model/enumeration/DownloadTimeType.java b/src/id/iptek/utms/agent/model/enumeration/DownloadTimeType.java new file mode 100644 index 0000000..4cb8726 --- /dev/null +++ b/src/id/iptek/utms/agent/model/enumeration/DownloadTimeType.java @@ -0,0 +1,32 @@ +package id.iptek.utms.agent.model.enumeration; + +/** + * + * @author jakar + */ +public enum DownloadTimeType { + + NEXT_CONTACT(1), + DATE_TIME(2); + + private final int value; + + private DownloadTimeType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static DownloadTimeType fromValue(int value) { + DownloadTimeType val = null; + for(DownloadTimeType _val : DownloadTimeType.values()) { + if(_val.getValue() == value) { + val = _val; + break; + } + } + return val; + } +} diff --git a/src/id/iptek/utms/agent/model/enumeration/InstallationNotificationType.java b/src/id/iptek/utms/agent/model/enumeration/InstallationNotificationType.java new file mode 100644 index 0000000..a6d8d6f --- /dev/null +++ b/src/id/iptek/utms/agent/model/enumeration/InstallationNotificationType.java @@ -0,0 +1,32 @@ +package id.iptek.utms.agent.model.enumeration; + +/** + * + * @author jakar + */ +public enum InstallationNotificationType { + + SILENT(1), + NEED_CONFIRMATION(2); + + private final int value; + + private InstallationNotificationType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static InstallationNotificationType fromValue(int value) { + InstallationNotificationType val = null; + for(InstallationNotificationType _val : InstallationNotificationType.values()) { + if(_val.getValue() == value) { + val = _val; + break; + } + } + return val; + } +} diff --git a/src/id/iptek/utms/agent/model/enumeration/InstallationTimeType.java b/src/id/iptek/utms/agent/model/enumeration/InstallationTimeType.java new file mode 100644 index 0000000..00d360a --- /dev/null +++ b/src/id/iptek/utms/agent/model/enumeration/InstallationTimeType.java @@ -0,0 +1,32 @@ +package id.iptek.utms.agent.model.enumeration; + +/** + * + * @author jakar + */ +public enum InstallationTimeType { + + IMMEDIATE_AFTER_DOWNLOAD(1), + DATE_TIME(2); + + private final int value; + + private InstallationTimeType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static InstallationTimeType fromValue(int value) { + InstallationTimeType val = null; + for(InstallationTimeType _val : InstallationTimeType.values()) { + if(_val.getValue() == value) { + val = _val; + break; + } + } + return val; + } +} diff --git a/src/id/iptek/utms/agent/model/enumeration/PublishTimeType.java b/src/id/iptek/utms/agent/model/enumeration/PublishTimeType.java new file mode 100644 index 0000000..e5af05f --- /dev/null +++ b/src/id/iptek/utms/agent/model/enumeration/PublishTimeType.java @@ -0,0 +1,32 @@ +package id.iptek.utms.agent.model.enumeration; + +/** + * + * @author jakar + */ +public enum PublishTimeType { + + IMMEDIATE(1), + DATE_TIME(2); + + private final int value; + + private PublishTimeType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static PublishTimeType fromValue(int value) { + PublishTimeType val = null; + for(PublishTimeType _val : PublishTimeType.values()) { + if(_val.getValue() == value) { + val = _val; + break; + } + } + return val; + } +} diff --git a/src/id/iptek/utms/agent/model/enumeration/TaskActivity.java b/src/id/iptek/utms/agent/model/enumeration/TaskActivity.java new file mode 100644 index 0000000..9d0871e --- /dev/null +++ b/src/id/iptek/utms/agent/model/enumeration/TaskActivity.java @@ -0,0 +1,40 @@ +package id.iptek.utms.agent.model.enumeration; + +/** + * + * @author jakar + */ +public enum TaskActivity { + + INITIAL(0), + PUBLISHED(1), + ACCEPTED(2), + DOWNLOAD_STARTED(3), + DOWNLOAD_SUCCESS(4), + DOWNLOAD_FAIL(5), + INSTALL_STARTED(6), + INSTALL_SUCCESS(7), + INSTALL_FAIL(8), + EXPIRED(99); + + private final int value; + + private TaskActivity(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static TaskActivity fromValue(int value) { + TaskActivity val = null; + for(TaskActivity _val : TaskActivity.values()) { + if(_val.getValue() == value) { + val = _val; + break; + } + } + return val; + } +} diff --git a/src/id/iptek/utms/agent/model/enumeration/TaskStatus.java b/src/id/iptek/utms/agent/model/enumeration/TaskStatus.java new file mode 100644 index 0000000..ec5b312 --- /dev/null +++ b/src/id/iptek/utms/agent/model/enumeration/TaskStatus.java @@ -0,0 +1,36 @@ +package id.iptek.utms.agent.model.enumeration; + +/** + * + * @author jakar + */ +public enum TaskStatus { + + INITIAL(0), + NOT_STARTED(1), + IN_PROGRESS(2), + CANCELLED(3), + DONE(4), + EXPIRED(99); + + private final int value; + + private TaskStatus(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static TaskStatus fromValue(int value) { + TaskStatus val = null; + for(TaskStatus _val : TaskStatus.values()) { + if(_val.getValue() == value) { + val = _val; + break; + } + } + return val; + } +} diff --git a/src/id/iptek/utms/agent/queue/ConsumerMode.java b/src/id/iptek/utms/agent/queue/ConsumerMode.java new file mode 100644 index 0000000..33ecb42 --- /dev/null +++ b/src/id/iptek/utms/agent/queue/ConsumerMode.java @@ -0,0 +1,11 @@ +package id.iptek.utms.agent.queue; + +/** + * + * @author jakar + */ +public enum ConsumerMode { + + SINGLE, BATCH; + +} diff --git a/src/id/iptek/utms/agent/queue/DelayMessageQueue.java b/src/id/iptek/utms/agent/queue/DelayMessageQueue.java new file mode 100644 index 0000000..3ff5014 --- /dev/null +++ b/src/id/iptek/utms/agent/queue/DelayMessageQueue.java @@ -0,0 +1,212 @@ +package id.iptek.utms.agent.queue; + +import id.iptek.utms.agent.db.DatabaseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + * @param + */ +public class DelayMessageQueue { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private ExecutorService executorservice; + private int capacity = 10; + private ConsumerMode mode = ConsumerMode.SINGLE; + private QueueMessageHandler handler; + private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + private boolean running = false; + private int totalThread = 1; + private ExecutorService internalExec = null; + private Future internalWorker = null; + private long batchFlushIntervalMillis = 1000L; + + public DelayMessageQueue() { + } + + public DelayMessageQueue(ExecutorService executorservice, QueueMessageHandler handler) { + this(executorservice, handler, 10); + } + + public DelayMessageQueue(ExecutorService executorservice, QueueMessageHandler handler, int capacity) { + this.executorservice = executorservice; + this.handler = handler; + this.capacity = capacity; + } + + public void setExecutorservice(ExecutorService executorservice) { + this.executorservice = executorservice; + } + + public void setCapacity(int capacity) { + this.capacity = capacity; + } + + public int getCapacity() { + return capacity; + } + + public void setMode(ConsumerMode mode) { + this.mode = mode; + } + + public void setHandler(QueueMessageHandler handler) { + this.handler = handler; + } + + public void setTotalThread(int totalThread) { + this.totalThread = totalThread; + } + + public void setBatchFlushIntervalMillis(long batchFlushIntervalMillis) { + this.batchFlushIntervalMillis = batchFlushIntervalMillis; + } + + public void run() { + logger.info("Run queue worker ..."); + this.running = true; + + internalExec = Executors.newFixedThreadPool(totalThread); + internalWorker = this.executorservice.submit(new QueueRunnable()); + } + + public void stop(boolean interrupt) { + logger.info("Stopping queue worker ..."); + stop(interrupt, 0L, TimeUnit.MILLISECONDS); + } + + public boolean stop(boolean interrupt, long timeout, TimeUnit unit) { + logger.info("Stopping queue worker ..."); + this.running = false; + if(internalWorker == null) { + logger.warn("Queue worker has not been run!"); + return true; + } + try { + if(interrupt) { + internalWorker.cancel(true); + } else if(!internalWorker.isDone()) { + if(timeout > 0L) { + internalWorker.get(timeout, unit); + } else { + internalWorker.get(); + } + } + } catch(Exception ex) { + logger.warn("Queue worker did not stop gracefully: {}", ex.getMessage()); + } + + internalExec.shutdown(); + try { + boolean terminated = timeout > 0L ? internalExec.awaitTermination(timeout, unit) : internalExec.awaitTermination(0L, TimeUnit.MILLISECONDS); + if(!terminated) { + logger.warn("Queue handler tasks did not finish within {} {}", timeout, unit); + internalExec.shutdownNow(); + return false; + } + } catch(InterruptedException ex) { + logger.warn("Interrupted while waiting queue handler tasks: {}", ex.getMessage()); + internalExec.shutdownNow(); + Thread.currentThread().interrupt(); + return false; + } + return true; + } + + public boolean add(T message) { + return queue.add(message); + } + + class QueueRunnable implements Runnable { + + private final List internalList = new ArrayList<>(); + private long lastFlushTime = System.currentTimeMillis(); + + @Override + public void run() { + while(running || !queue.isEmpty()) { + try { + T message = queue.poll(); + if(message != null) { + if(mode == ConsumerMode.BATCH) { + // handled batches + internalList.add(message); + + // check capacity + if(internalList.size() >= capacity) { + flushBatch(); + } + } else if(mode == ConsumerMode.SINGLE) { + // run job using separate runnable + Runnable run = new Runnable() { + @Override + public void run() { + try { + // handle single + boolean handled = handler.handleMessage(message); + logger.debug("Message handled? {}", handled); + } catch(DatabaseException ex) { + logger.error("Error saving heartbeats: {}", ex.getMessage(), ex); + } + } + }; + internalExec.execute(run); + } + } else { + if(mode == ConsumerMode.BATCH && !internalList.isEmpty() + && System.currentTimeMillis() - lastFlushTime >= batchFlushIntervalMillis) { + flushBatch(); + } + TimeUnit.MILLISECONDS.sleep(100L); + } + if(mode == ConsumerMode.BATCH && !internalList.isEmpty() + && System.currentTimeMillis() - lastFlushTime >= batchFlushIntervalMillis) { + flushBatch(); + } + } catch(InterruptedException ex) { + logger.debug("Queue worker interrupted: {}", ex.getMessage()); + Thread.currentThread().interrupt(); + break; + } catch(Exception ex) { + logger.error("Error consuming queue : {}", ex.getMessage(), ex); + } + } + if(mode == ConsumerMode.BATCH && !internalList.isEmpty()) { + flushBatch(); + } + } + + private void flushBatch() { + List workingList = new ArrayList<>(); + workingList.addAll(internalList); + internalList.clear(); + lastFlushTime = System.currentTimeMillis(); + + // run job using separate runnable + Runnable run = new Runnable() { + @Override + public void run() { + try { + boolean handled = handler.handleMessages(workingList); + logger.debug("Batch messages handled? {}", handled); + } catch(DatabaseException ex) { + logger.error("Error saving heartbeats: {}", ex.getMessage(), ex); + } + } + }; + internalExec.execute(run); + } + + } +} diff --git a/src/id/iptek/utms/agent/queue/DeviceInitQueueMessageHandler.java b/src/id/iptek/utms/agent/queue/DeviceInitQueueMessageHandler.java new file mode 100644 index 0000000..db1a70f --- /dev/null +++ b/src/id/iptek/utms/agent/queue/DeviceInitQueueMessageHandler.java @@ -0,0 +1,134 @@ +package id.iptek.utms.agent.queue; + +import com.google.gson.Gson; +import id.iptek.utms.agent.dao.ProfileDao; +import id.iptek.utms.agent.model.Profile; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public class DeviceInitQueueMessageHandler implements QueueMessageHandler { + + public final static String NAME = "DEV_INIT_QUEUE_HANDLER"; + private Logger logger = LoggerFactory.getLogger(getClass()); + private ProfileDao profileDao = new ProfileDao(); + private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm"); + + public DeviceInitQueueMessageHandler() { + } + + @Override + public boolean handleMessage(Map message) { + boolean processed = false; + try { + logger.debug("Handling init request: {}", message); + long start = System.currentTimeMillis(); + + MqttClient mqttClient = (MqttClient) message.get("MQTT"); + String terminalSN = (String) message.get("device_sn"); + Profile profile = profileDao.getProfileForTerminal(terminalSN); + if (profile != null) { + broadcastProfile(mqttClient, profile); + } else { + logger.warn("No profile for SN: {}", terminalSN); + } + processed = true; + long end = System.currentTimeMillis(); + logger.debug("Saved in {}ms", (end-start)); + return processed; + } catch(Exception ex) { + logger.error("Error handling init: {}", ex.getMessage(), ex); + return false; + } + } + + @Override + public boolean handleMessages(List messages) { + try { + logger.debug("Handling list of init requests: {}", messages.size()); + long start = System.currentTimeMillis(); + boolean processed = false; + for(Map message : messages) { + MqttClient mqttClient = (MqttClient) message.get("MQTT"); + String terminalSN = (String) message.get("device_sn"); + Profile profile = profileDao.getProfileForTerminal(terminalSN); + if (profile != null) { + broadcastProfile(mqttClient, profile); + } else { + logger.warn("No profile for SN: {}", terminalSN); + } + processed |= true; + } + long end = System.currentTimeMillis(); + logger.debug("Saved in {}ms", (end-start)); + return processed; + } catch(Exception ex) { + logger.error("Error handling list of init requests: {}", ex.getMessage(), ex); + return false; + } + } + + // send profile to device topic via mqtt + public void broadcastProfile(MqttClient mqttClient, + Profile profile) throws Exception { + logger.debug("Publish profile ..."); + try { + try { + Gson gson = new Gson(); + int qos = 2; + String topicName = profile.getTerminalSN().toUpperCase() + "_IN"; + // generate json + Map messageMap = new HashMap<>(); + messageMap.put("req_id", UUID.randomUUID().toString()); + messageMap.put("req_time", dateTimeFormat.format(new Date())); + messageMap.put("req_type", "UPDATE_PARAM"); + // profile map + Map profileMap = new HashMap<>(); + profileMap.put("id", profile.getId()); + profileMap.put("name", profile.getName()); + profileMap.put("hearbeat_interval", profile.getHeartbeatInterval()); + profileMap.put("diagnostic_interval", profile.getDiagnosticInterval()); + profileMap.put("mask_home_button", profile.isMaskHomeButton()); + profileMap.put("mask_status_button", profile.isMaskStatusButton()); + profileMap.put("schedule_reboot", profile.isScheduleReboot()); + if (profile.isScheduleReboot()) { + profileMap.put("schedule_reboot_time", timeFormat.format(profile.getScheduleRebootTime())); + } + profileMap.put("relocation_alert", profile.isRelocationAlert()); + profileMap.put("moving_threshold", profile.getMovingThreshold()); + profileMap.put("admin_password", profile.getAdminPassword()); + + // additional + profileMap.put("front_app", profile.getFrontApp()); + profileMap.put("apps_home_list", profile.getApps()); + profileMap.put("group_list", profile.getGroupIds()); + + messageMap.put("profile", profileMap); + + String messageData = gson.toJson(messageMap); + + logger.debug("Try publish message to: {}", topicName); + mqttClient.publish(topicName, messageData.getBytes(), qos, false); + logger.debug("Message published!"); + } catch (Exception ex) { + logger.error("Error publish to device: {}", ex.getMessage(), ex); + } finally { + } + } finally { + logger.debug("Publish profile DONE"); + } + } + +} diff --git a/src/id/iptek/utms/agent/queue/DiagnosticInfoQueueMessageHandler.java b/src/id/iptek/utms/agent/queue/DiagnosticInfoQueueMessageHandler.java new file mode 100644 index 0000000..4b08112 --- /dev/null +++ b/src/id/iptek/utms/agent/queue/DiagnosticInfoQueueMessageHandler.java @@ -0,0 +1,52 @@ +package id.iptek.utms.agent.queue; + +import id.iptek.utms.agent.dao.DiagnosticDao; +import id.iptek.utms.agent.model.Diagnostic; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public class DiagnosticInfoQueueMessageHandler implements QueueMessageHandler { + + public final static String NAME = "DIAGNOSTIC_QUEUE_HANDLER"; + private Logger logger = LoggerFactory.getLogger(getClass()); + private DiagnosticDao dao = new DiagnosticDao(); + + public DiagnosticInfoQueueMessageHandler() { + } + + @Override + public boolean handleMessage(Diagnostic message) { + try { + logger.debug("Saving diagnostic: {}", message); + long start = System.currentTimeMillis(); + boolean saved = dao.save(message); + long end = System.currentTimeMillis(); + logger.debug("Saved in {}ms", (end-start)); + return saved; + } catch(Exception ex) { + logger.error("Error handling diagnostic: {}", ex.getMessage(), ex); + return false; + } + } + + @Override + public boolean handleMessages(List messages) { + try { + logger.debug("Saving list of diagnostics: {}", messages.size()); + long start = System.currentTimeMillis(); + boolean saved = dao.saveAll(messages); + long end = System.currentTimeMillis(); + logger.debug("Saved in {}ms", (end-start)); + return saved; + } catch(Exception ex) { + logger.error("Error handling list of diagnostics: {}", ex.getMessage(), ex); + return false; + } + } + +} diff --git a/src/id/iptek/utms/agent/queue/HeartBeatQueueMessageHandler.java b/src/id/iptek/utms/agent/queue/HeartBeatQueueMessageHandler.java new file mode 100644 index 0000000..d2fa2d3 --- /dev/null +++ b/src/id/iptek/utms/agent/queue/HeartBeatQueueMessageHandler.java @@ -0,0 +1,52 @@ +package id.iptek.utms.agent.queue; + +import id.iptek.utms.agent.dao.HeartbeatDao; +import id.iptek.utms.agent.model.HeartBeat; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public class HeartBeatQueueMessageHandler implements QueueMessageHandler { + + public final static String NAME = "HEARTBEAT_QUEUE_HANDLER"; + private Logger logger = LoggerFactory.getLogger(getClass()); + private HeartbeatDao dao = new HeartbeatDao(); + + public HeartBeatQueueMessageHandler() { + } + + @Override + public boolean handleMessage(HeartBeat message) { + try { + logger.debug("Saving heartbeat: {}", message); + long start = System.currentTimeMillis(); + boolean saved = dao.save(message); + long end = System.currentTimeMillis(); + logger.debug("Saved in {}ms", (end-start)); + return saved; + } catch(Exception ex) { + logger.error("Error handling heartbeat: {}", ex.getMessage(), ex); + return false; + } + } + + @Override + public boolean handleMessages(List messages) { + try { + logger.debug("Saving list of heartbeats: {}", messages.size()); + long start = System.currentTimeMillis(); + boolean saved = dao.saveAll(messages); + long end = System.currentTimeMillis(); + logger.debug("Saved in {}ms", (end-start)); + return saved; + } catch(Exception ex) { + logger.error("Error handling list of heartbeats: {}", ex.getMessage(), ex); + return false; + } + } + +} diff --git a/src/id/iptek/utms/agent/queue/QueueMessageHandler.java b/src/id/iptek/utms/agent/queue/QueueMessageHandler.java new file mode 100644 index 0000000..be0b093 --- /dev/null +++ b/src/id/iptek/utms/agent/queue/QueueMessageHandler.java @@ -0,0 +1,25 @@ +package id.iptek.utms.agent.queue; + +import java.util.List; + +/** + * + * @author jakar + */ +public interface QueueMessageHandler { + + /** + * Handle message + * @param message + * @return + */ + public boolean handleMessage(T message); + + /** + * Handle batch of messages at same time + * @param messages + * @return + */ + public boolean handleMessages(List messages); + +} diff --git a/src/id/iptek/utms/agent/sample/MqttPublishSample.java b/src/id/iptek/utms/agent/sample/MqttPublishSample.java new file mode 100644 index 0000000..dc51ad2 --- /dev/null +++ b/src/id/iptek/utms/agent/sample/MqttPublishSample.java @@ -0,0 +1,52 @@ +package id.iptek.utms.agent.sample; + +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + +public class MqttPublishSample { + + public static void main(String[] args) { + + String topic = "SERVER_IN_DIAG"; + String content = "{\"req_id\":\"a971d68e-1fcc-4eeb-ba96-6c4e6e867a06\",\"req_type\":\"DIAGNOSTIC\",\"req_time\":\"20250827140806\",\"device_sn\":\"V9E0191945\",\"diagnostic_info\":{\"imei\":\"863188045199949\",\"meid\":\"\",\"battery_temp\":\" 250\",\"battery_percentage\":\"100\",\"total_memory\":\"1997537280\",\"available_memory\":\"1308291072\",\"total_flash_memory\":\"16000000000B\",\"available_flash_memory\":\"11429019648\",\"total_mobile_data\":\"1248910\",\"switching_times\":\"0\",\"current_boot_time\":\"0\",\"total_boot_time\":\"104\",\"total_length_printed\":\"13097.0\",\"swiping_card_times\":\"297\",\"dip_inserting_times\":\"273\",\"nfc_card_reading_times\":\"124\",\"front_camera_open_times\":\"0\",\"rear_camera_open_times\":\"0\",\"new_diagnostic\":true,\"sam_available\":true},\"location_info\":{\"lat\":-6.357999801635742,\"lng\":106.81169891357422},\"installed_apps\":[{\"app_name\":\"CPU Info\",\"package_name\":\"com.kgurgul.cpuinfo\",\"version\":\"6.0.0\"},{\"app_name\":\"NSICCSDemo\",\"package_name\":\"com.unified.nsiccsdemo\",\"version\":\"1.0\"},{\"app_name\":\"VFSystemService\",\"package_name\":\"com.vfi.smartpos.system_service\",\"version\":\"1.14.0.1\"},{\"app_name\":\"Telnet\",\"package_name\":\"koushikdutta.telnet\",\"version\":\"1.0.1\"},{\"app_name\":\"Athena\",\"package_name\":\"dev.sebaubuntu.athena.dev\",\"version\":\"1.1.0\"},{\"app_name\":\"UTMS\",\"package_name\":\"com.unified.launcher\",\"version\":\"1.4.3.BIT\"},{\"app_name\":\"Bank Index\",\"package_name\":\"com.iptek.index\",\"version\":\"1.1.4.DEV\"},{\"app_name\":\"VFService\",\"package_name\":\"com.vfi.smartpos.deviceservice\",\"version\":\"3.11.7.1\"}],\"device_info_ex\":{\"SN\":\"V9E0191945\",\"PN\":\"M550-104-22-EUA-6\",\"IMSI\":\"510019134244830\",\"IMEI\":\"863188045199949\",\"MEID\":\"\",\"manufacture\":\"Verifone\",\"deviceModel\":\"X990\",\"androidOsVer\":\"10\",\"androidKernalVer\":\"Linux version 4.14.133 (linbx@v-dev) (gcc version 4.9.x 20150123 (prerelease) (GCC) ) #1 SMP PREEMPT Thu Aug 4 11:30:37 CST 2022\",\"romVer\":\"3A.1.385(202208041133 INTL)\",\"firmwareVer\":\"V3.1.0.20220804\",\"hardwareVer\":\"M550-10X-XX-XXX-6 Rev.C\",\"SPVer\":\"X990-V1.0.26(202209080934)\",\"VFSerivceVer\":\"3.11.7.1\",\"VRKSn\":\"\",\"SponsorID\":\"\",\"SponsorName\":\"\",\"bootVer\":\"X990-1.0.0.A10(20220707)\"}}"; + //String content = "{\"req_id\":\"7484442f-1d5c-4d51-96bc-ed7cb0ec8b95\",\"req_time\":\"2023-07-30 18:39:00\",\"req_type\":\"RESET_CONNECTION\"}"; + int qos = 2; + String broker = "tcp://192.168.4.112:1883"; + String clientId = "JavaSample"; + MemoryPersistence persistence = new MemoryPersistence(); + + try { + MqttClient sampleClient = new MqttClient(broker, clientId, persistence); + MqttConnectOptions connOpts = new MqttConnectOptions(); + connOpts.setCleanSession(true); + connOpts.setUserName("user1"); + connOpts.setPassword("P@ssw0rd".toCharArray()); + //connOpts.setUserName("wira"); + //connOpts.setPassword("wira1234".toCharArray()); + + System.out.println("Connecting to broker: "+broker); + sampleClient.connect(connOpts); + System.out.println("Connected"); + System.out.println("Publishing message: "+content); + MqttMessage message = new MqttMessage(content.getBytes()); + message.setQos(qos); + //message.setRetained(true); + sampleClient.publish(topic, message); + System.out.println("Message published"); + sampleClient.disconnect(); + System.out.println("Disconnected"); + System.exit(0); + } catch(MqttException me) { + System.out.println("reason "+me.getReasonCode()); + System.out.println("msg "+me.getMessage()); + System.out.println("loc "+me.getLocalizedMessage()); + System.out.println("cause "+me.getCause()); + System.out.println("excep "+me); + me.printStackTrace(); + } + } + +} diff --git a/src/id/iptek/utms/agent/sample/MqttSubscribeSample.java b/src/id/iptek/utms/agent/sample/MqttSubscribeSample.java new file mode 100644 index 0000000..fc76393 --- /dev/null +++ b/src/id/iptek/utms/agent/sample/MqttSubscribeSample.java @@ -0,0 +1,87 @@ +package id.iptek.utms.agent.sample; + +import java.util.concurrent.TimeUnit; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + +public class MqttSubscribeSample { + + public static void main(String[] args) { + + final String topic = "5354b64f-c52c-45e0-8b26-49b5d5352e44"; + final String topic2 = "V1E0352316_IN"; + //V1E0352316_IN + int qos = 2; + //String broker = "tcp://verifone.id:1883"; + String broker = "tcp://bit.unifiedtms.id:1883"; + String clientId = "SN02"; + MemoryPersistence persistence = new MemoryPersistence(); + + try { + final MqttClient sampleClient = new MqttClient(broker, clientId, persistence); + MqttConnectOptions connOpts = new MqttConnectOptions(); + connOpts.setCleanSession(true); + //connOpts.setUserName("wira"); + //connOpts.setPassword("wira1234".toCharArray()); + connOpts.setUserName("user1"); + connOpts.setPassword("P@ssw0rd".toCharArray()); + connOpts.setAutomaticReconnect(true); + connOpts.setKeepAliveInterval(10000); + System.out.println("Connecting to broker: " + broker); + sampleClient.setCallback(new MqttCallbackExtended() { + @Override + public void connectComplete(boolean bln, String string) { + System.out.println("Connect complete? " + bln + " : " + string); + try { + sampleClient.subscribe(topic); + sampleClient.subscribe(topic2); + } catch(MqttException ex) { + System.err.println("Error subscribe topic: " + topic); + } + } + + @Override + public void connectionLost(Throwable thrwbl) { + System.err.println("Connection Lost: " + thrwbl); + } + + @Override + public void messageArrived(String topic, MqttMessage mm) throws Exception { + System.out.println("<< [" + topic + "] " + new String(mm.getPayload()) + " (" + mm.getId() + " - " + mm.getQos() + ")"); + } + + @Override + public void deliveryComplete(IMqttDeliveryToken imdt) { + System.err.println("Delivery Complete: " + imdt.getMessageId()); + } + + }); + sampleClient.connect(connOpts); + System.out.println("Connected"); + //sampleClient.subscribe(topic); + + try { + TimeUnit.SECONDS.sleep(100); + } catch(InterruptedException ex) { + ex.printStackTrace(); + } + + sampleClient.disconnect(); + System.out.println("Disconnected"); + System.exit(0); + } catch (MqttException me) { + System.out.println("reason " + me.getReasonCode()); + System.out.println("msg " + me.getMessage()); + System.out.println("loc " + me.getLocalizedMessage()); + System.out.println("cause " + me.getCause()); + System.out.println("excep " + me); + me.printStackTrace(); + } + } + +} diff --git a/src/id/iptek/utms/agent/scheduler/job/DownloadTaskPublisherJob.java b/src/id/iptek/utms/agent/scheduler/job/DownloadTaskPublisherJob.java new file mode 100644 index 0000000..c884aa2 --- /dev/null +++ b/src/id/iptek/utms/agent/scheduler/job/DownloadTaskPublisherJob.java @@ -0,0 +1,299 @@ +package id.iptek.utms.agent.scheduler.job; + +import com.google.gson.Gson; +import id.iptek.utms.agent.AppConfig; +import id.iptek.utms.agent.dao.ApplicationDao; +import id.iptek.utms.agent.dao.DownloadTaskDao; +import id.iptek.utms.agent.model.ApplicationExt; +import id.iptek.utms.agent.model.DownloadTask; +import id.iptek.utms.agent.model.PendingDownloadTask; +import id.iptek.utms.agent.model.enumeration.TaskStatus; +import id.iptek.utms.agent.util.Singleton; +import io.minio.GetPresignedObjectUrlArgs; +import io.minio.MinioClient; +import io.minio.http.Method; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import org.apache.commons.codec.digest.DigestUtils; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public class DownloadTaskPublisherJob implements Job { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + private ApplicationDao appDao = new ApplicationDao(); + private DownloadTaskDao dao = new DownloadTaskDao(); + private static final SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + private static final SimpleDateFormat sdfYEAR = new SimpleDateFormat("yyyy"); + private static final SimpleDateFormat sdfMONTH = new SimpleDateFormat("MM"); + private static final SimpleDateFormat sdfDATE = new SimpleDateFormat("dd"); + + public DownloadTaskPublisherJob() { + } + + @Override + public void execute(JobExecutionContext jec) throws JobExecutionException { + try { + logger.info("Execute publish task job .."); + JobDataMap dataMap = jec.getMergedJobDataMap(); + // get task + DownloadTask task = (DownloadTask) dataMap.get("task"); + logger.info(">> {}", task); + if(task.getStatus() == TaskStatus.INITIAL) { + boolean populated = dao.populateAndBuildDownloadTaskLogs(task); + logger.info("Populated? {}", populated); + } + List groupPendingDownloadTasks = dao.getUnpublishedPendingTaskByTerminalGroup(task); + List pendingDownloadTasks = dao.getUnpublishedPendingTaskByTerminal(task); + + MqttClient mqttClient = (MqttClient) Singleton.getInstance("MQTT_CLIENT").getObject(); + logger.info("Total Group Pending Download Tasks: {}", groupPendingDownloadTasks.size()); + for(PendingDownloadTask downloadTask : groupPendingDownloadTasks) { + logger.info(">> [1] task={}, group={}, app={}", downloadTask.getId(), downloadTask.getTerminalGroupId(), downloadTask.getApp().getId()); + broadcastDownloadTasksToDevice(mqttClient, downloadTask); + } + logger.info("Total Pending Download Tasks: {}", pendingDownloadTasks.size()); + for(PendingDownloadTask downloadTask : pendingDownloadTasks) { + logger.info(">> [2] task={}, terminal={}, app={}", downloadTask.getId(), downloadTask.getTerminalSN(), downloadTask.getApp().getId()); + broadcastDownloadTasksToDevice(mqttClient, downloadTask); + } + } catch(Exception ex) { + logger.error("Error publish task: {}", ex.getMessage(), ex); + } finally {} + } + + // send download task to device topic via mqtt + protected void broadcastDownloadTasksToDevice(MqttClient mqttClient, + PendingDownloadTask task) throws Exception { + logger.debug("Broadcast tasks ..."); + try { + int qos = 2; + String topicName; + // broadcast to group or specific SN + boolean broadcastToGroup = false; + if(task.getTerminalGroupId() != null) { + topicName = task.getTerminalGroupId(); + broadcastToGroup = true; + } else { + topicName = task.getTerminalSN().toUpperCase() + "_IN"; + } + + AppConfig appConfig = AppConfig.getInstance(); + Date now = Calendar.getInstance().getTime(); + ApplicationExt app = (ApplicationExt) task.getApp(); + String bucketName = appConfig.get("fileserver.bucket"); + // check icon url expiry + Date iconUrlExp = app.getIconUrlExp(); + if(iconUrlExp == null || now.after(iconUrlExp)) { + logger.debug("Icon url expired: {}", iconUrlExp); + String iconUniqueName = app.getUniqueIconName(); + + String path = appConfig.get("fileserver.icon.path"); + int expiry = appConfig.getAsInt("fileserver.icon.expiry", 7); + TimeUnit timeUnit = getTimeUnit(appConfig, "fileserver.icon.timeunit", "server.icon.timeunit", "DAYS"); + Date iconExpiryDate = getExpiryDate(now, expiry, timeUnit); + + logger.info("Generate icon url for app '{}', old expiry: {}", app.getId(), iconUrlExp); + String iconDownloadUrl = generateDownloadUrl(bucketName, path, iconUniqueName, + expiry, timeUnit); + app.setIconUrl(iconDownloadUrl); + app.setIconUrlExp(iconExpiryDate); + logger.info("Icon url generated for app '{}', new expiry: {}", app.getId(), iconExpiryDate); + + boolean iconUrlUpdated = appDao.updateIconUrl(app); + logger.info("Icon url updated? {}", iconUrlUpdated); + } + // check download url expiry + Date downloadUrlExp = app.getDownloadUrlExp(); + if(downloadUrlExp == null || now.after(downloadUrlExp)) { + logger.info("Download app url expired for task '{}', app '{}', old expiry: {}", task.getId(), app.getId(), downloadUrlExp); + String uniqueName = app.getUniqueName(); + + String path = appConfig.get("fileserver.app.path"); + int expiry = appConfig.getAsInt("fileserver.app.expiry", 7); + TimeUnit timeUnit = getTimeUnit(appConfig, "fileserver.app.timeunit", "server.app.timeunit", "DAYS"); + Date appExpiryDate = getExpiryDate(now, expiry, timeUnit); + + logger.info("Generate app url for task '{}', app '{}'", task.getId(), app.getId()); + String appDownloadUrl = generateDownloadUrl(bucketName, path, uniqueName, + expiry, timeUnit); + app.setDownloadUrl(appDownloadUrl); + app.setDownloadUrlExp(appExpiryDate); + logger.info("App url generated for task '{}', app '{}', new expiry: {}", task.getId(), app.getId(), appExpiryDate); + + boolean appUrlUpdated = appDao.updateAppUrl(task, app); + logger.info("App url updated? {}", appUrlUpdated); + } + + // generate json + Gson gson = new Gson(); + Map messageMap = new HashMap<>(); + messageMap.put("req_id", UUID.randomUUID().toString()); + messageMap.put("req_time", dateTimeFormat.format(new Date())); + // we'll use log's id as ack_id + if(broadcastToGroup) { + String ackId = "BROADCAST_" + task.getId(); + messageMap.put("ack_id", ackId); + } else { + messageMap.put("ack_id", task.getLogId()); + } + messageMap.put("req_type", "DOWNLOAD_APP_TASK"); + // task map + Map taskMap = new HashMap<>(); + taskMap.put("id", task.getId()); + taskMap.put("name", task.getName()); + taskMap.put("download_time_type", getDownloadTimeTypeName(task.getDownloadTimeType())); + if (task.getDownloadTimeType() == 2) { + taskMap.put("download_time", dateTimeFormat.format(task.getDownloadTime())); + } + taskMap.put("installation_time_type", getInstallationTimeTypeName(task.getInstallationTimeType())); + if (task.getInstallationTimeType() == 2) { + taskMap.put("installation_time", dateTimeFormat.format(task.getInstallationTime())); + } + taskMap.put("installation_notification", getInstallationNotificationName(task.getInstallationNotification())); + messageMap.put("task", taskMap); + // app map + Map appMap = new HashMap<>(); + appMap.put("id", app.getId()); + appMap.put("name", app.getAppName()); + appMap.put("package_name", app.getPackageName()); + appMap.put("version", app.getVersion()); + appMap.put("company", app.getCompanyName()); + appMap.put("uninstallable", app.isUninstallable()); + appMap.put("description", app.getDescription()); + appMap.put("download_url", app.getDownloadUrl()); + appMap.put("md5_checksum", app.getChecksum()); + messageMap.put("app", appMap); + + String messageData = gson.toJson(messageMap); + logger.debug("Try publish message to: {}", topicName); + logger.debug(">> {}", messageData); + mqttClient.publish(topicName, messageData.getBytes(), qos, false); + logger.debug("Message published!"); + + // change log's last broadcast time + if(broadcastToGroup) { + boolean updated = dao.updateGroupLastBroadcastTs(task); + logger.debug("Download task group last broadcast time updated? {}", updated); + } else { + boolean updated = dao.updateTerminalLastBroadcastTs(task); + logger.debug("Download task terminal last broadcast time updated? {}", updated); + } + } finally { + logger.debug("Start broadcast download tasks DONE"); + } + } + + + + private String getDownloadTimeTypeName(int type) { + switch (type) { + case 1: + return "NEXT_CONTACT"; + case 2: + return "DATETIME"; + } + return null; + } + + private String getInstallationTimeTypeName(int type) { + switch (type) { + case 1: + return "IMMEDIATE_AFTER_DOWNLOAD"; + case 2: + return "DATETIME"; + } + return null; + } + + private String getInstallationNotificationName(int type) { + switch (type) { + case 1: + return "SILENT"; + case 2: + return "NEED_CONFIRMATION"; + } + return null; + } + + private String getFilePath(String fileId, String fileExt, Date fileCreateDate) { + String filePath = AppConfig.getInstance().get("file.dir") + File.separator + + sdfYEAR.format(fileCreateDate) + File.separator + + sdfMONTH.format(fileCreateDate) + File.separator + + sdfDATE.format(fileCreateDate) + File.separator + + fileId + "." + fileExt; + return filePath; + } + + private String md5Checksum(byte[] bytes) throws Exception { + try { + String checksum = DigestUtils.md5Hex(bytes); + return checksum; + } finally { + } + } + + private String generateDownloadUrl(String bucketName, String path, String uniqueName, int expiry, TimeUnit expiryTimeUnit) throws Exception { + MinioClient minioClient = (MinioClient) Singleton.getInstance("MINIO_CLIENT").getObject(); + String url + = minioClient.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Method.GET) + .bucket(bucketName) + .object(path + "/" + uniqueName) + .expiry(expiry, expiryTimeUnit) + .build()); + return url; + } + + private Date getExpiryDate(Date now, int expiry, TimeUnit timeUnit) { + Calendar cal = Calendar.getInstance(); + cal.setTime(now); + switch (timeUnit) { + case MILLISECONDS: + cal.add(Calendar.MILLISECOND, expiry); + break; + case SECONDS: + cal.add(Calendar.SECOND, expiry); + break; + case MINUTES: + cal.add(Calendar.MINUTE, expiry); + break; + case HOURS: + cal.add(Calendar.HOUR_OF_DAY, expiry); + break; + case DAYS: + cal.add(Calendar.DAY_OF_MONTH, expiry); + break; + } + return cal.getTime(); + } + + private TimeUnit getTimeUnit(AppConfig appConfig, String name, String legacyName, String defaultValue) { + String value = appConfig.get(name); + if(value == null) { + value = appConfig.get(legacyName, defaultValue); + } + return TimeUnit.valueOf(value.trim().toUpperCase()); + } +} diff --git a/src/id/iptek/utms/agent/scheduler/job/DownloadTaskSchedulerJob.java b/src/id/iptek/utms/agent/scheduler/job/DownloadTaskSchedulerJob.java new file mode 100644 index 0000000..056816e --- /dev/null +++ b/src/id/iptek/utms/agent/scheduler/job/DownloadTaskSchedulerJob.java @@ -0,0 +1,120 @@ +package id.iptek.utms.agent.scheduler.job; + +import id.iptek.utms.agent.dao.DownloadTaskDao; +import id.iptek.utms.agent.model.DownloadTask; +import id.iptek.utms.agent.model.enumeration.PublishTimeType; +import id.iptek.utms.agent.util.Singleton; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import org.quartz.Job; +import static org.quartz.JobBuilder.newJob; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author jakar + */ +public class DownloadTaskSchedulerJob implements Job { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + private DownloadTaskDao dao = new DownloadTaskDao(); + + public DownloadTaskSchedulerJob() { + } + + @Override + public void execute(JobExecutionContext jec) throws JobExecutionException { + try { + logger.info("Execute job .."); + List tasks = dao.getUnscheduledTasks(); + logger.info("Done query"); + logger.info("Got {} tasks", tasks.size()); + /*List scheduledJobIds = (List) Singleton.getInstance("SCHEDULER_JOBS").getObject(); + if(scheduledJobIds == null) { + scheduledJobIds = new ArrayList<>(); + Singleton.getInstance("SCHEDULER_JOBS").setObject(scheduledJobIds); + }*/ + for(DownloadTask task : tasks) { + /*if(scheduledJobIds.contains(task.getId())) { + logger.info("Download Task with id '{}' is already scheduled.", task.getId()); + } else { + logger.info("Will schedule download task with id: {}", task.getId()); + scheduleTask(task); + scheduledJobIds.add(task.getId()); + }*/ + + logger.info("Will schedule download task with id: {}", task.getId()); + scheduleTask(task); + } + } catch(Exception ex) { + logger.error("Error get tasks: {}", ex.getMessage(), ex); + } finally {} + } + + private void scheduleTask(DownloadTask task) throws SchedulerException { + // get quartz scheduler + Scheduler sched = (Scheduler) Singleton.getInstance("SCHEDULER").getObject(); + JobKey jobkey = new JobKey("download-task-" + task.getId(), "publish-task"); + if(sched.checkExists(jobkey)) return; + + if(task.getPublishTimeType() == PublishTimeType.IMMEDIATE) { + // publish job in next 1 minutes + JobDataMap map = new JobDataMap(); + map.put("task", task); + JobDetail job = newJob(DownloadTaskPublisherJob.class) + .withIdentity(jobkey) + .usingJobData(map) + .build(); + Calendar time = Calendar.getInstance(); + time.add(Calendar.MINUTE, 1); + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity("download-task-trigger-" + task.getId(), "publish-task") + .startAt(time.getTime()) + .build(); + sched.scheduleJob(job, trigger); + + logger.info("Task id-{} scheduled @{}", task.getId(), trigger.getFinalFireTime()); + } else if(task.getPublishTimeType() == PublishTimeType.DATE_TIME) { + Calendar time = Calendar.getInstance(); + // compare publish time with current time + Date publishTime = task.getPublishTime(); + JobDataMap map = new JobDataMap(); + map.put("task", task); + JobDetail job = newJob(DownloadTaskPublisherJob.class) + .withIdentity(jobkey) + .usingJobData(map) + .build(); + if(time.getTime().equals(publishTime) || time.getTime().after(publishTime)) { + // publish it next 5 minutes + time.add(Calendar.MINUTE, 1); + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity("download-task-trigger-" + task.getId(), "publish-task") + .startAt(time.getTime()) + .build(); + sched.scheduleJob(job, trigger); + + logger.info("Task id-{} scheduled @{}", task.getId(), trigger.getFinalFireTime()); + } else { + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity("download-task-trigger-" + task.getId(), "publish-task") + .startAt(publishTime) + .build(); + sched.scheduleJob(job, trigger); + + logger.info("Task id-{} scheduled @{}", task.getId(), trigger.getFinalFireTime()); + } + } + } +} diff --git a/src/id/iptek/utms/agent/test/TestMinio.java b/src/id/iptek/utms/agent/test/TestMinio.java new file mode 100644 index 0000000..443058d --- /dev/null +++ b/src/id/iptek/utms/agent/test/TestMinio.java @@ -0,0 +1,51 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package id.iptek.utms.agent.test; + +import io.minio.GetPresignedObjectUrlArgs; +import io.minio.MinioClient; +import io.minio.http.Method; +import java.util.concurrent.TimeUnit; + +/** + * + * @author jakar + */ +public class TestMinio { + + public static void main(String[] args) throws Exception { + MinioClient minioClient + = MinioClient.builder() + .endpoint("https://download.unifiedtms.id:9000") + .credentials("i9ZB0sNhHaiQe7oEP0sL", "4bxVnKwKgkXpZJ9Dou4oprZ1E8oe3s1HuvKLRn0N") + .build(); + + String uniqueName = "1693968324774app_ico.png"; + String uniqueApkName = "1693223977836Facebook Lite_368.0.0.5.95_Apkpure.apk"; + + String url + = minioClient.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Method.GET) + .bucket("application-bucket") + .object("icons/" + uniqueName) + .expiry(1, TimeUnit.MINUTES) + .build()); + System.out.println("URL: " + url); + + String apkUrl + = minioClient.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Method.GET) + .bucket("application-bucket") + .object("apps/" + uniqueApkName) + .expiry(1, TimeUnit.MINUTES) + .build()); + System.out.println("APK URL: " + apkUrl); + + System.exit(0); + } + +} diff --git a/src/id/iptek/utms/agent/test/TestSubstring.java b/src/id/iptek/utms/agent/test/TestSubstring.java new file mode 100644 index 0000000..e2ca8a8 --- /dev/null +++ b/src/id/iptek/utms/agent/test/TestSubstring.java @@ -0,0 +1,18 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package id.iptek.utms.agent.test; + +/** + * + * @author jakar + */ +public class TestSubstring { + + public static void main(String[] args) { + String ackId = "BROADCAST-1234"; + System.out.println(">> " + ackId.substring(10)); + } + +} diff --git a/src/id/iptek/utms/agent/util/FileUtil.java b/src/id/iptek/utms/agent/util/FileUtil.java new file mode 100644 index 0000000..9f04664 --- /dev/null +++ b/src/id/iptek/utms/agent/util/FileUtil.java @@ -0,0 +1,135 @@ +package id.iptek.utms.agent.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jaka Ramdani + */ +public class FileUtil { + + private static Logger log = LoggerFactory.getLogger(FileUtil.class); + + private FileUtil() { + } + + public static void copy(String srcPath, String dstPath) throws IOException, Exception { + copy(srcPath, dstPath, false); + } + + public static void copy(String srcPath, String dstPath, boolean overwrite) throws IOException, Exception { + log.debug("\"{}\" -> \"{}\"", srcPath, dstPath); + File srcFile = new File(srcPath); + File dstFile = new File(dstPath); + + if (srcFile.exists() && !srcFile.isDirectory()) { + if (dstFile.exists() && dstFile.isDirectory()) { + String[] _filenames = srcPath.split("[\\/\\\\]"); + dstFile = new File(dstFile, _filenames[_filenames.length - 1]); + } else if (dstFile.exists() && !overwrite) { + throw new IOException("Destination file \"" + dstPath + "\" is exists, not overwriting file."); + } else if (dstFile.exists()) { + //remove existing file + if (!dstFile.delete()) { + throw new IOException("Can not remove old destination file \"" + dstPath + "\"."); + } + } + //create new file + byte[] srcBytes = StreamUtil.read(new FileInputStream(srcFile)); + StreamUtil.write(new FileOutputStream(dstFile), srcBytes, true); + } else { + throw new IOException("Source file \"" + srcFile + "\" is not exists or is a directory."); + } + } + + public static void move(String srcPath, String dstPath) throws IOException, Exception { + move(srcPath, dstPath, false); + } + + public static void move(String srcPath, String dstPath, boolean overwrite) throws IOException, Exception { + copy(srcPath, dstPath, overwrite); + delete(srcPath); + } + + public static boolean delete(String path) throws IOException, Exception { + File srcFile = new File(path); + if (srcFile.exists() && !srcFile.isDirectory()) { + boolean deleted = new File(path).delete(); + if (deleted) { + log.debug("\"{}\" removed", path); + } else { + log.debug("\"{}\" NOT removed", path); + } + return deleted; + } else { + throw new IOException("Source file \"" + srcFile + "\" is not exists or is a directory."); + } + } + + public static String[] list(String path) throws IOException, Exception { + log.debug("list: \"{}\"", path); + return new File(path).list(); + } + + public static String[] list(String path, FilenameFilter fileNameFilter) throws IOException, Exception { + log.debug("list: \"{}\"", path); + return new File(path).list(fileNameFilter); + } + + public static File[] listFiles(File parent, FilenameFilter fileNameFilter) throws IOException, Exception { + return listFiles(parent, fileNameFilter, false); + } + + public static File[] listFiles(File parent, FilenameFilter fileNameFilter, boolean nameOrdered) throws IOException, Exception { + return listFiles(parent.getAbsolutePath(), fileNameFilter, nameOrdered); + } + + public static File[] listFiles(String path, FilenameFilter fileNameFilter) throws IOException, Exception { + return listFiles(path, fileNameFilter, false); + } + + public static File[] listFiles(String path, FilenameFilter fileNameFilter, boolean nameOrdered) throws IOException, Exception { + log.debug("list: \"{}\"", path); + File[] files = new File(path).listFiles(fileNameFilter); + if (nameOrdered) { + Arrays.sort(files, new Comparator() { + + @Override + public int compare(File f1, File f2) { + return f2.getName().compareTo(f1.getName()); + } + }); + } + return files; + } + + public static boolean mkdirs(String path) throws IOException, Exception { + return mkdirs(path, false); + } + + public static boolean mkdirs(String path, boolean silentOnExists) throws IOException, Exception { + File file = new File(path); + if (file.exists()) { + if (silentOnExists) { + log.debug("\"{}\" is already exists. keep silence ..", path); + return true; + } + throw new IOException("\"" + path + "\" is already exists."); + } + boolean created = file.mkdirs(); + if (created) { + log.debug("\"{}\" created", path); + } else { + log.debug("\"{}\" NOT created", path); + } + return created; + } +} diff --git a/src/id/iptek/utms/agent/util/IOUtil.java b/src/id/iptek/utms/agent/util/IOUtil.java new file mode 100644 index 0000000..32f0e9e --- /dev/null +++ b/src/id/iptek/utms/agent/util/IOUtil.java @@ -0,0 +1,71 @@ +package id.iptek.utms.agent.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; + +/** + * + * @author Jaka Ramdani + */ +public final class IOUtil { + + private IOUtil() { + } + + public static byte[] read(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + int i = -1; + byte[] buffer = new byte[1024*4]; + while( (i = in.read(buffer)) != -1 ) { + baos.write(buffer, 0, i); + } + baos.flush(); + } finally { + close(in); + } + return baos.toByteArray(); + } + + + + public static void close(InputStream in) { + if(in != null) { + try { + in.close(); + } catch(IOException ignore) {} + in = null; + } + } + + public static void close(OutputStream out) { + if(out != null) { + try { + out.close(); + } catch(IOException ignore) {} + out = null; + } + } + + public static void close(Reader reader) { + if(reader != null) { + try { + reader.close(); + } catch(IOException ignore) {} + reader = null; + } + } + + public static void write(byte[] bytes, OutputStream out) throws IOException { + out.write(bytes); + out.flush(); + out.close(); + } + + public static void write(String text, OutputStream out) throws IOException { + out.write(text.getBytes()); + } +} diff --git a/src/id/iptek/utms/agent/util/Singleton.java b/src/id/iptek/utms/agent/util/Singleton.java new file mode 100644 index 0000000..d248f06 --- /dev/null +++ b/src/id/iptek/utms/agent/util/Singleton.java @@ -0,0 +1,29 @@ +package id.iptek.utms.agent.util; + +import java.util.HashMap; +import java.util.Map; + +public final class Singleton { + + private final static Map INSTANCES = new HashMap<>(); + private Object object; + + private Singleton() {} + + public void setObject(Object object) { + this.object = object; + } + + public Object getObject() { + return object; + } + + public static Singleton getInstance(String name) { + Singleton instance = INSTANCES.get(name); + if(instance == null) { + instance = new Singleton(); + INSTANCES.put(name, instance); + } + return instance; + } +} diff --git a/src/id/iptek/utms/agent/util/StreamUtil.java b/src/id/iptek/utms/agent/util/StreamUtil.java new file mode 100644 index 0000000..ccfff77 --- /dev/null +++ b/src/id/iptek/utms/agent/util/StreamUtil.java @@ -0,0 +1,96 @@ +package id.iptek.utms.agent.util; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.commons.codec.binary.Base64; + +/** + * + * @author Jaka Ramdani + */ +public final class StreamUtil { + + private StreamUtil() { + } + + public static String readAsBase64(InputStream is) throws IOException { + byte[] bytes = read(is); + return Base64.encodeBase64String(bytes); + } + + public static byte[] read(InputStream is) throws IOException { + return read(is, true); + } + + public static byte[] read(InputStream is, boolean close) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + byte[] buffer = new byte[10*1024]; + int i = -1; + while( (i = is.read(buffer)) > 0 ) { + baos.write(buffer, 0, i); + } + baos.flush(); + } finally { + if(is != null && close) { + try { + is.close(); + } catch(IOException ignore) {} + is = null; + } + } + return baos.toByteArray(); + } + + public static void write(OutputStream os, byte[] bytes, boolean close) throws IOException { + try { + int maxPerWrite = 10*1024; + int len = bytes.length; + int written = 0; + while(written < len) { + int write = (len - written) >= maxPerWrite ? maxPerWrite : (len - written); + os.write(bytes, written, write); + written += write; + } + os.flush(); + } finally { + if(os != null && close) { + try { + os.close(); + } catch(IOException ignore) {} + os = null; + } + } + } + + public static void close(InputStream in) { + if(in != null) { + try { + in.close(); + } catch(IOException ignore) {} + in = null; + } + } + + public static void close(OutputStream out) { + if(out != null) { + try { + out.close(); + } catch(IOException ignore) {} + out = null; + } + } + + public static void main(String[] args) { + try { + String file = "G:\\Pictures\\Avatar-Me.jpg"; + String imageBase64 = readAsBase64(new FileInputStream(file)); + System.out.println("ImageBase64:\n" + imageBase64); + } catch(Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/id/iptek/utms/agent/util/StringUtil.java b/src/id/iptek/utms/agent/util/StringUtil.java new file mode 100644 index 0000000..0973927 --- /dev/null +++ b/src/id/iptek/utms/agent/util/StringUtil.java @@ -0,0 +1,134 @@ +package id.iptek.utms.agent.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.id.random.SessionIdGenerator; + +/** + * + * @author Jaka Ramdani + */ +public final class StringUtil { + + private StringUtil() { + } + + public static String decodeHtml(String strValue) { + String strResult = ""; + if (strValue != null) { + String strInfo = strValue; + StringBuffer sbResult = null; + try { + sbResult = new StringBuffer(strInfo); + + int intStart = 0; + int intEnd = 0; + while (true) { + if (strInfo.indexOf("<") != -1) { + intStart = strInfo.indexOf("<"); + intEnd = intStart + "<".length(); + + sbResult.replace(intStart, intEnd, "<"); + strInfo = sbResult.toString(); + continue; + } + if (strInfo.indexOf(">") != -1) { + intStart = strInfo.indexOf(">"); + intEnd = intStart + ">".length(); + + sbResult.replace(intStart, intEnd, ">"); + strInfo = sbResult.toString(); + continue; + } + if (strInfo.indexOf("&") != -1) { + intStart = strInfo.indexOf("&"); + intEnd = intStart + "&".length(); + + sbResult.replace(intStart, intEnd, "&"); + strInfo = sbResult.toString(); + continue; + } + if (strInfo.indexOf(""") != -1) { + intStart = strInfo.indexOf("""); + intEnd = intStart + """.length(); + + sbResult.replace(intStart, intEnd, "\""); + strInfo = sbResult.toString(); + continue; + } + if (strInfo.indexOf(" ") == -1) { + break; + } + intStart = strInfo.indexOf(" "); + intEnd = intStart + " ".length(); + + sbResult.replace(intStart, intEnd, " "); + strInfo = sbResult.toString(); + } + + strResult = sbResult.toString(); + } catch (Exception e) { + strResult = ""; + } finally { + sbResult = null; + } + } + return strResult; + } + + public StringBuilder collect(String msg) { + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + for (int i = 0; i < msg.length(); i++) { + char currentChar = msg.charAt(i); + if ((currentChar == '\n') || (currentChar == '\n')) { + sb.append("
"); + } else if (currentChar == ' ') { + sb.append(" "); + } else { + sb.append(currentChar); + } + } + sb.append("\n"); + sb.append("\n"); + return sb; + } + + public static String getMD5Hash(String string) { + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(string.getBytes()); + byte[] digest = md5.digest(); + string = Base64.encodeBase64String(digest); + } catch (NoSuchAlgorithmException e1) { + e1.printStackTrace(); + } + + return string; + } + + private static String byteArrToHexString(byte[] bArr) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bArr.length; i++) { + int unsigned = bArr[i] & 0xff; + if (unsigned < 0x10) { + sb.append("0"); + } + sb.append(Integer.toHexString((unsigned))); + } + return sb.toString(); + } + + public static void main(String[] args) { + SessionIdGenerator gidGenerator = new SessionIdGenerator(); + String gid = DigestUtils.md5Hex(gidGenerator.nextStringIdentifier()); + System.err.println(">> gid: " + gid); + } +} diff --git a/src/id/iptek/utms/agent/worker/DeviceInitWorker.java b/src/id/iptek/utms/agent/worker/DeviceInitWorker.java new file mode 100644 index 0000000..bc71d40 --- /dev/null +++ b/src/id/iptek/utms/agent/worker/DeviceInitWorker.java @@ -0,0 +1,58 @@ +package id.iptek.utms.agent.worker; + +import com.google.gson.Gson; +import id.iptek.utms.agent.AppConfig; +import id.iptek.utms.agent.queue.ConsumerMode; +import id.iptek.utms.agent.queue.DelayMessageQueue; +import id.iptek.utms.agent.queue.DeviceInitQueueMessageHandler; +import id.iptek.utms.agent.util.Singleton; +import java.util.Map; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +/** + * + * @author Jaka + */ +public class DeviceInitWorker extends Worker { + + public DeviceInitWorker() { + } + + @Override + void init(String consumerName, AppConfig appConfig) throws Exception { + super.init(consumerName, appConfig); + + DelayMessageQueue queue = (DelayMessageQueue) Singleton.getInstance(DeviceInitQueueMessageHandler.NAME).getObject(); + ConsumerMode mode = ConsumerMode.valueOf(appConfig.get("mqtt.consumer."+consumerName+".consumermode", "SINGLE")); + int batchCapacity = appConfig.getAsInt("mqtt.consumer."+consumerName+".batch.capacity", 10); + queue.setCapacity(batchCapacity); + queue.setMode(mode); + } + + @Override + public void messageArrived(String string, MqttMessage mm) throws Exception { + try { + String json = new String(mm.getPayload()); + logger.debug("<< " + json + " (" + mm.getId() + " - " + mm.getQos() + ")"); + Gson gson = new Gson(); + Map map = gson.fromJson(json, Map.class); + + String terminalSN = (String) map.get("device_sn"); + if(terminalSN == null) { + logger.debug("SN Information null!"); + return; + } + + // put mqttclient to the map + map.put("MQTT", mqttClient); + + DelayMessageQueue queue = (DelayMessageQueue) Singleton.getInstance(DeviceInitQueueMessageHandler.NAME).getObject(); + boolean queued = queue.add(map); + if(!queued) { + logger.error("Init request NOT queued: {}", queued); + } + } catch (Exception ex) { + logger.error("Init: {}", ex.getMessage(), ex); + } + } +} diff --git a/src/id/iptek/utms/agent/worker/DiagnosticWorker.java b/src/id/iptek/utms/agent/worker/DiagnosticWorker.java new file mode 100644 index 0000000..07292d1 --- /dev/null +++ b/src/id/iptek/utms/agent/worker/DiagnosticWorker.java @@ -0,0 +1,184 @@ +package id.iptek.utms.agent.worker; + +import com.google.gson.Gson; +import id.iptek.utms.agent.AppConfig; +import id.iptek.utms.agent.model.ApplicationSimple; +import id.iptek.utms.agent.model.Diagnostic; +import id.iptek.utms.agent.model.DiagnosticInfo; +import id.iptek.utms.agent.model.LocationInfo; +import id.iptek.utms.agent.queue.ConsumerMode; +import id.iptek.utms.agent.queue.DelayMessageQueue; +import id.iptek.utms.agent.queue.DiagnosticInfoQueueMessageHandler; +import id.iptek.utms.agent.util.Singleton; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +public class DiagnosticWorker extends Worker { + + @Override + void init(String consumerName, AppConfig appConfig) throws Exception { + super.init(consumerName, appConfig); + + DelayMessageQueue queue = (DelayMessageQueue) Singleton.getInstance(DiagnosticInfoQueueMessageHandler.NAME).getObject(); + ConsumerMode mode = ConsumerMode.valueOf(appConfig.get("mqtt.consumer."+consumerName+".consumermode", "SINGLE")); + int batchCapacity = appConfig.getAsInt("mqtt.consumer."+consumerName+".batch.capacity", 10); + queue.setCapacity(batchCapacity); + queue.setMode(mode); + } + + @Override + public void messageArrived(String string, MqttMessage mm) throws Exception { + try { + String json = new String(mm.getPayload()); + this.logger.debug("<< " + json + " (" + mm.getId() + " - " + mm.getQos() + ")"); + Gson gson = new Gson(); + + Map diagnosticReqMap = (Map) gson.fromJson(json, Map.class); + if(!"DIAGNOSTIC".equals((String) diagnosticReqMap.get("req_type"))) { + // skip message + return; + } + + Diagnostic diagnosticReq = mapDiagnosticData(diagnosticReqMap); + // set id + diagnosticReq.setId(UUID.randomUUID().toString()); + diagnosticReq.setCreateTime(new Date()); + + DelayMessageQueue queue = (DelayMessageQueue) Singleton.getInstance(DiagnosticInfoQueueMessageHandler.NAME).getObject(); + boolean queued = queue.add(diagnosticReq); + if(!queued) { + logger.error("Diagnostic NOT queued: {}", queued); + } + } catch (Exception ex) { + this.logger.error("Error saving diagnostic: {}", ex.getMessage(), ex); + } + } + + private Diagnostic mapDiagnosticData(Map map) { + String reqId = (String) map.get("req_id"); + String terminalSN = (String) map.get("device_sn"); + Diagnostic diagnostic = new Diagnostic(); + diagnostic.setId(UUID.randomUUID().toString()); + diagnostic.setReqId(reqId); + diagnostic.setDeviceSn(terminalSN); + Map diagnosticInfoMap = (Map) map.get("diagnostic_info"); + boolean isNewDiagnostic = getBoolean(diagnosticInfoMap, "new_diagnostic"); + DiagnosticInfo diagnosticInfo = new DiagnosticInfo(); + diagnostic.setDiagnosticInfo(diagnosticInfo); + diagnosticInfo.setBatteryPercentage(getInt(diagnosticInfoMap, "battery_percentage")); + double temp = getDouble(diagnosticInfoMap, "battery_temp"); + if (isNewDiagnostic) { + temp /= 10.0D; + } + diagnosticInfo.setBatteryTemp(temp); + diagnosticInfo.setMeid((String) diagnosticInfoMap.get("meid")); + diagnosticInfo.setTotalMemory((String) diagnosticInfoMap.get("total_memory")); + diagnosticInfo.setAvailableMemory((String) diagnosticInfoMap.get("available_memory")); + diagnosticInfo.setTotalFlashMemory((String) diagnosticInfoMap.get("total_flash_memory")); + diagnosticInfo.setAvailableFlashMemory((String) diagnosticInfoMap.get("available_flash_memory")); + diagnosticInfo.setTotalMobileData((String) diagnosticInfoMap.get("total_mobile_data")); + diagnosticInfo.setCurrentBootTime(getInt(diagnosticInfoMap, "current_boot_time")); + diagnosticInfo.setTotalBootTime(getInt(diagnosticInfoMap, "total_boot_time")); + diagnosticInfo.setTotalLengthPrinted(getDouble(diagnosticInfoMap, "total_length_printed")); + diagnosticInfo.setSwitchingTimes(getInt(diagnosticInfoMap, "switching_times")); + diagnosticInfo.setSwipingCardTimes(getInt(diagnosticInfoMap, "swiping_card_times")); + diagnosticInfo.setDipInsertingTimes(getInt(diagnosticInfoMap, "dip_inserting_times")); + diagnosticInfo.setNfcCardReadingTimes(getInt(diagnosticInfoMap, "nfc_card_reading_times")); + diagnosticInfo.setFrontCameraOpenTimes(getInt(diagnosticInfoMap, "front_camera_open_times")); + diagnosticInfo.setRearCameraOpenTimes(getInt(diagnosticInfoMap, "rear_camera_open_times")); + diagnosticInfo.setChargeTimes(getInt(diagnosticInfoMap, "charge_times")); + List> installedApps = (List>) map.get("installed_apps"); + if (installedApps != null) { + List applications = new ArrayList<>(); + installedApps.forEach(appMap -> { + String appName = getString(appMap, "app_name"); + String packageName = getString(appMap, "package_name"); + String version = getString(appMap, "version"); + ApplicationSimple appSimple = new ApplicationSimple(); + appSimple.setId(UUID.randomUUID().toString()); + appSimple.setAppName(appName); + appSimple.setPackageName(packageName); + appSimple.setVersion(version); + applications.add(appSimple); + }); + diagnostic.setInstalledApps(applications); + Gson gson = new Gson(); + String installedAppString = gson.toJson(applications); + diagnosticInfo.setInstalledAppString(installedAppString); + } + Map locationInfoMap = (Map) map.get("location_info"); + LocationInfo locationInfo = new LocationInfo(); + diagnostic.setLocationInfo(locationInfo); + locationInfo.setLat((Double) locationInfoMap.get("lat")); + locationInfo.setLng((Double) locationInfoMap.get("lng")); + return diagnostic; + } + + private boolean getBoolean(Map map, String key) { + Object val = map.get(key); + if (val instanceof String) { + String sVal = (String) val; + return Boolean.parseBoolean(sVal.trim()); + } + if (val instanceof Boolean) { + return ((Boolean) val).booleanValue(); + } + return false; + } + + private double getDouble(Map map, String key) { + Object val = map.get(key); + if (val instanceof String) { + String sVal = (String) val; + return Double.parseDouble(sVal.trim()); + } + if (val instanceof Double) { + return ((Double) val).doubleValue(); + } + if (val instanceof Integer) { + return ((Double) val).doubleValue(); + } + return 0.0D; + } + + private int getInt(Map map, String key) { + Object val = map.get(key); + if (val instanceof String) { + String sVal = (String) val; + return Integer.parseInt(sVal.trim()); + } + if (val instanceof Integer) { + return ((Integer) val).intValue(); + } + if (val instanceof Double) { + return ((Double) val).intValue(); + } + return 0; + } + + private long getLong(Map map, String key) { + Object val = map.get(key); + if (val instanceof String) { + String sVal = (String) val; + if (sVal.contains("B")) { + sVal = sVal.replace("B", ""); + } + return Long.parseLong(sVal.trim()); + } + if (val instanceof Long) { + return ((Long) val).longValue(); + } + if (val instanceof Double) { + return ((Double) val).longValue(); + } + return 0L; + } + + private String getString(Map map, String key) { + return (String) map.get(key); + } +} diff --git a/src/id/iptek/utms/agent/worker/DownloadTaskAckWorker.java b/src/id/iptek/utms/agent/worker/DownloadTaskAckWorker.java new file mode 100644 index 0000000..5befd61 --- /dev/null +++ b/src/id/iptek/utms/agent/worker/DownloadTaskAckWorker.java @@ -0,0 +1,165 @@ +package id.iptek.utms.agent.worker; + +import com.google.gson.Gson; +import id.iptek.utms.agent.GenericCache; +import id.iptek.utms.agent.dao.DownloadTaskDao; +import id.iptek.utms.agent.dao.TerminalDao; +import id.iptek.utms.agent.model.TaskAckReq; +import id.iptek.utms.agent.model.TerminalIdObj; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +/** + * + * @author Jaka + */ +public class DownloadTaskAckWorker extends Worker { + + private DownloadTaskDao dao = new DownloadTaskDao(); + private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + private TerminalDao terminalDao = new TerminalDao(); + + public DownloadTaskAckWorker() { + } + + @Override + public void messageArrived(String string, MqttMessage mm) throws Exception { + try { + String json = new String(mm.getPayload()); + logger.debug("<< " + json + " (" + mm.getId() + " - " + mm.getQos() + ")"); + Gson gson = new Gson(); + Map map = gson.fromJson(json, Map.class); + + String reqId = UUID.randomUUID().toString(); + String requestType = (String) map.get("req_type"); + if(!"DOWNLOAD_ACK".equals(requestType)) { + return; + } + + String terminalSN = (String) map.get("device_sn"); + String ackId = (String) map.get("ack_id"); + String status = (String) map.get("status"); + if("ACCEPTED".equals(status)) { + status = "NOT_STARTED"; + } + int activity = translateActivityFromStatus(status); + + TaskAckReq req = new TaskAckReq(); + req.setReqId(reqId); + req.setAckId(ackId); + req.setTerminalSN(terminalSN); + req.setActivity(activity); + if(ackId.startsWith("BROADCAST_")) { + String taskId = ackId.substring(10); + req.setBroadcasted(true); + req.setTaskId(taskId); + } + // get terminal id from cache + String terminalId = getTerminalIdBySN(terminalSN); + if(terminalId != null) { + req.setTerminalId(terminalId); + boolean saved = dao.save(req); + logger.debug("Download task ACK request saved? {}", saved); + if(saved) { + if("INSTALL_SUCCESS".equals(status) || + "INSTALL_FAIL".equals(status)) { + // send ack DOWNLOAD_TASK_CONFIRM_ACK + broadcastDownloadTasksConfirmACK(mqttClient, terminalSN, ackId); + } + } + } else { + logger.warn("Invalid SN: {}", terminalSN); + } + } catch(Exception ex) { + logger.error("Download task ACK: {}", ex.getMessage(), ex); + } + } + + private int translateActivityFromStatus(String status) { + int activity = 0; // initial + switch(status) { + case "INITIAL": + activity = 0; + break; + case "NOT_STARTED": + activity = 1; + break; + case "DOWNLOAD_STARTED": + activity = 2; + break; + case "DOWNLOAD_SUCCESS": + activity = 3; + break; + case "DOWNLOAD_FAIL": + activity = 4; + break; + case "INSTALL_STARTED": + activity = 5; + break; + case "INSTALL_SUCCESS": + activity = 6; + break; + case "INSTALL_FAIL": + activity = 7; + break; + case "ACCEPTED": + activity = 8; + break; + } + return activity; + } + + // send download task to device topic via mqtt + public void broadcastDownloadTasksConfirmACK(MqttClient mqttClient, + String terminalSN, + String ackId) throws Exception { + logger.debug("Broadcast tasks ..."); + try { + try { + int qos = 2; + String topicName = terminalSN.toUpperCase() + "_IN"; + // generate json + Gson gson = new Gson(); + Map messageMap = new HashMap<>(); + messageMap.put("req_id", UUID.randomUUID().toString()); + messageMap.put("req_time", dateTimeFormat.format(new Date())); + // we'll use log's id as ack_id + messageMap.put("ack_id", ackId); + messageMap.put("req_type", "DOWNLOAD_TASK_CONFIRM_ACK"); + + String messageData = gson.toJson(messageMap); + + logger.debug("Try publish message to: {}", topicName); + mqttClient.publish(topicName, messageData.getBytes(), qos, false); + logger.debug("Message published!"); + } catch(Exception ex) { + logger.error("Error publish to device: {}", ex.getMessage(), ex); + } finally { + } + } finally { + logger.debug("Start broadcast download tasks DONE"); + } + } + + private String getTerminalIdBySN(String sn) throws Exception { + try { + GenericCache cache = GenericCache.getCache("terminalId"); + String terminalId = (String) cache.get(sn.toLowerCase()); + if(terminalId == null) { + // load from db first + TerminalIdObj terminalObj = terminalDao.getTerminalIdBySN(sn); + if(terminalObj != null) { + terminalId = terminalObj.getTerminalId(); + cache.put(sn.toLowerCase(), terminalId); + } + } + return terminalId; + } finally {} + } +} diff --git a/src/id/iptek/utms/agent/worker/HeartbeatWorker.java b/src/id/iptek/utms/agent/worker/HeartbeatWorker.java new file mode 100644 index 0000000..042c94c --- /dev/null +++ b/src/id/iptek/utms/agent/worker/HeartbeatWorker.java @@ -0,0 +1,64 @@ +package id.iptek.utms.agent.worker; + +import com.google.gson.Gson; +import id.iptek.utms.agent.AppConfig; +import id.iptek.utms.agent.model.HeartBeat; +import id.iptek.utms.agent.queue.ConsumerMode; +import id.iptek.utms.agent.queue.DelayMessageQueue; +import id.iptek.utms.agent.queue.HeartBeatQueueMessageHandler; +import id.iptek.utms.agent.util.Singleton; +import java.util.Date; +import java.util.UUID; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +/** + * + * @author Jaka + */ +public class HeartbeatWorker extends Worker { + + public HeartbeatWorker() { + } + + @Override + void init(String consumerName, AppConfig appConfig) throws Exception { + super.init(consumerName, appConfig); + + DelayMessageQueue queue = (DelayMessageQueue) Singleton.getInstance(HeartBeatQueueMessageHandler.NAME).getObject(); + ConsumerMode mode = ConsumerMode.valueOf(appConfig.get("mqtt.consumer."+consumerName+".consumermode", "SINGLE")); + int batchCapacity = appConfig.getAsInt("mqtt.consumer."+consumerName+".batch.capacity", 10); + queue.setCapacity(batchCapacity); + queue.setMode(mode); + } + + @Override + protected void doSomething() throws Exception { + // do something + } + + @Override + public void messageArrived(String string, MqttMessage mm) throws Exception { + try { + String json = new String(mm.getPayload()); + logger.debug("<< " + json + " (" + mm.getId() + " - " + mm.getQos() + ")"); + Gson gson = new Gson(); + HeartBeat heartbeatReq = gson.fromJson(json, HeartBeat.class); + if(!"HEARTBEAT".equals(heartbeatReq.getReqType())) { + // skip message + return; + } + + // set id + heartbeatReq.setId(UUID.randomUUID().toString()); + heartbeatReq.setCreateTime(new Date()); + + DelayMessageQueue queue = (DelayMessageQueue) Singleton.getInstance(HeartBeatQueueMessageHandler.NAME).getObject(); + boolean queued = queue.add(heartbeatReq); + if(!queued) { + logger.error("Heartbeat NOT queued: {}", queued); + } + } catch (Exception ex) { + logger.error("Error processing heartbeat: {}", ex.getMessage(), ex); + } + } +} diff --git a/src/id/iptek/utms/agent/worker/Worker.java b/src/id/iptek/utms/agent/worker/Worker.java new file mode 100644 index 0000000..66dc168 --- /dev/null +++ b/src/id/iptek/utms/agent/worker/Worker.java @@ -0,0 +1,623 @@ +package id.iptek.utms.agent.worker; + +import com.google.gson.Gson; +import id.iptek.utms.agent.AppConfig; +import id.iptek.utms.agent.GenericCache; +import id.iptek.utms.agent.dao.DeleteTaskDao; +import id.iptek.utms.agent.dao.DownloadTaskDao; +import id.iptek.utms.agent.model.Application; +import id.iptek.utms.agent.model.ApplicationSimple; +import id.iptek.utms.agent.model.PendingDeleteTask; +import id.iptek.utms.agent.model.PendingDownloadTask; +import id.iptek.utms.agent.model.SimplePendingDownloadTask; +import id.iptek.utms.agent.util.IOUtil; +import java.io.File; +import java.io.FileInputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang.RandomStringUtils; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jaka + */ +public abstract class Worker implements MqttCallbackExtended, Runnable { + + protected Logger logger; + private String brokerUrl; + private String loggingId; + private String topic; + private String clientIdPrefix; + private String user; + private String password; + private long sleep; + private long keepAlive; + private boolean cleanSession; + private int maxInflight; + private boolean autoReconnect; + private long reconnectDelayMillis; + + /** + * mqtt * + */ + private MemoryPersistence persistence = new MemoryPersistence(); + protected MqttClient mqttClient; + private volatile boolean running = false; + + private DeleteTaskDao deleteTaskDao = new DeleteTaskDao(); + private DownloadTaskDao downloadTaskDao = new DownloadTaskDao(); + private static final SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + private static final SimpleDateFormat sdfYEAR = new SimpleDateFormat("yyyy"); + private static final SimpleDateFormat sdfMONTH = new SimpleDateFormat("MM"); + private static final SimpleDateFormat sdfDATE = new SimpleDateFormat("dd"); + + protected ExecutorService executorService = null; + + public Worker() { + } + + public void setExecutorService(ExecutorService executorService) { + this.executorService = executorService; + } + + /** + * @return the brokerUrl + */ + public String getBrokerUrl() { + return brokerUrl; + } + + /** + * @return the loggingId + */ + public String getLoggingId() { + return loggingId; + } + + /** + * @return the topic + */ + public String getTopic() { + return topic; + } + + /** + * @return the clientIdPrefix + */ + public String getClientIdPrefix() { + return clientIdPrefix; + } + + /** + * @return the user + */ + public String getUser() { + return user; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @return the sleep + */ + public long getSleep() { + return sleep; + } + + /** + * @return the keepAlive + */ + public long getKeepAlive() { + return keepAlive; + } + + /** + * @return the cleanSession + */ + public boolean isCleanSession() { + return cleanSession; + } + + /** + * @return the maxInflight + */ + public int getMaxInflight() { + return maxInflight; + } + + /** + * @return the autoReconnect + */ + public boolean isAutoReconnect() { + return autoReconnect; + } + + void init(String consumerName, AppConfig appConfig) throws Exception { + String _brokerUrl = appConfig.get("mqtt.broker.url"); + String _loggingId = appConfig.get("mqtt.consumer." + consumerName + ".loggingid"); + String _topic = appConfig.get("mqtt.consumer." + consumerName + ".topic"); + String _clientIdPrefix = appConfig.get("mqtt.consumer." + consumerName + ".clientid_prefix"); + String _user = appConfig.get("mqtt.consumer." + consumerName + ".user"); + String _password = appConfig.get("mqtt.consumer." + consumerName + ".password"); + long _sleep = appConfig.getAsLong("mqtt.consumer." + consumerName + ".sleep"); + long _keepAlive = appConfig.getAsLong("mqtt.consumer." + consumerName + ".keepalive", 10L); + int _maxInflight = appConfig.getAsInt("mqtt.consumer." + consumerName + ".maxinflight", 32); + boolean _cleanSession = appConfig.getAsBoolean("mqtt.consumer." + consumerName + ".cleansession", true); + boolean _autoReconnect = appConfig.getAsBoolean("mqtt.consumer." + consumerName + ".autoreconnect", true); + long _reconnectDelayMillis = appConfig.getAsLong("mqtt.consumer." + consumerName + ".reconnect.delay.millis", 5000L); + + this.brokerUrl = _brokerUrl; + this.loggingId = _loggingId; + this.topic = _topic; + this.clientIdPrefix = _clientIdPrefix; + this.user = _user; + this.password = _password; + this.sleep = _sleep; + this.keepAlive = _keepAlive; + this.maxInflight = _maxInflight; + this.cleanSession = _cleanSession; + this.autoReconnect = _autoReconnect; + this.reconnectDelayMillis = _reconnectDelayMillis; + + // init logger + //logger = LoggerFactory.getLogger(getClass() + "." + _loggingId); + logger = LoggerFactory.getLogger(getClass()); + } + + public void start() throws Exception { + try { + this.running = true; + + logger.info("Starting ..."); + String clientId = getClientIdPrefix() + RandomStringUtils.randomAlphanumeric(10); + mqttClient = new MqttClient(brokerUrl, clientId, persistence); + + } finally { + } + } + + public void stop() throws Exception { + try { + this.running = false; + if (mqttClient != null) { + mqttClient.disconnectForcibly(0L, 250L, false); + mqttClient.close(true); + } + } finally { + // no-op + } + } + + @Override + public void connectionLost(Throwable thrwbl) { + logger.warn("Connection Lost: " + thrwbl); + } + + @Override + public void deliveryComplete(IMqttDeliveryToken imdt) { + // no-op + } + + @Override + public void connectComplete(boolean bln, String string) { + if (getTopic() != null) { + try { + logger.info("Connected, subscribe to '{}'", getTopic()); + mqttClient.subscribe(getTopic()); + } catch (Exception ex) { + logger.error("Error subscribe topic: " + topic); + } + } else { + logger.debug("No topic subscribed!"); + } + } + + @Override + public void run() { + do { + try { + if (mqttClient != null && !mqttClient.isConnected()) { + MqttConnectOptions connOpts = new MqttConnectOptions(); + connOpts.setCleanSession(isCleanSession()); + connOpts.setUserName(getUser()); + if (getPassword() != null) { + connOpts.setPassword(getPassword().toCharArray()); + } + connOpts.setAutomaticReconnect(isAutoReconnect()); + connOpts.setKeepAliveInterval((int) getKeepAlive()); + connOpts.setMaxInflight(getMaxInflight()); + mqttClient.setCallback(this); + logger.info("Started ..."); + + logger.info("Try to connect ..."); + try { + mqttClient.connect(connOpts); + } catch (Exception ex) { + logger.error("Error connect MQTT broker: {}", ex.getMessage(), ex); + sleepBeforeReconnect(); + continue; + } + } + + // do something + try { + doSomething(); + } catch (Exception ex) { + logger.error("Error doing something: {}", ex.getMessage(), ex); + } + + if (getSleep() != 0L) { + try { + logger.info("Sleep for {} ms", getSleep()); + TimeUnit.MILLISECONDS.sleep(getSleep()); + } catch (InterruptedException ex) { + logger.error("Sleep got interrupted: {}", ex.getMessage(), ex); + Thread.currentThread().interrupt(); + break; + } + } + } catch (Exception ex) { + logger.error("Error run: {}", ex.getMessage(), ex); + } + } while (running); + } + + private void sleepBeforeReconnect() { + try { + logger.info("Wait {} ms before reconnecting MQTT broker", reconnectDelayMillis); + TimeUnit.MILLISECONDS.sleep(reconnectDelayMillis); + } catch (InterruptedException ex) { + logger.error("Reconnect wait interrupted: {}", ex.getMessage(), ex); + Thread.currentThread().interrupt(); + running = false; + } + } + + protected void doSomething() throws Exception { + } + + /* + protected void publishPendingTasks(String terminalSN) { + boolean dummy = true; + if (dummy) { + return; + } + + // pending delete task + List pendingDeleteTaskList = deleteTaskDao.getPendingTaskForTerminal(terminalSN); + pendingDeleteTaskList.forEach((t) -> { + try { + broadcastDeleteTasksToDevice(mqttClient, t); + } catch (Exception ex) { + logger.error("Error push delete task: [{}, {}, {}]", t.getTerminalSN(), t.getId(), t.getLogId()); + } + }); + + // pending download task + List pendingDownloadTaskList = downloadTaskDao.getPendingTaskForTerminal(terminalSN); + pendingDownloadTaskList.forEach((t) -> { + try { + broadcastDownloadTasksToDevice(mqttClient, t); + } catch (Exception ex) { + logger.error("Error push download task: [{}, {}, {}]", t.getTerminalSN(), t.getId(), t.getLogId()); + } + }); + } + + protected void publishPendingTasksForAll(int limit, int periodeMinutes, boolean checkOnline) { + // pending delete task + Date currentTime = downloadTaskDao.getCurrentTime(); + logger.debug("Current server time: {}", currentTime); + logger.debug("Get all pending delete tasks .."); + List pendingDeleteTaskList = null; + + if(checkOnline) { + pendingDeleteTaskList = deleteTaskDao.getPendingTaskForOnlineTerminals(periodeMinutes, limit); + } else { + pendingDeleteTaskList = deleteTaskDao.getPendingTaskForTerminals(periodeMinutes, limit); + } + logger.debug("... got {} task item.", pendingDeleteTaskList.size()); + pendingDeleteTaskList.forEach((t) -> { + try { + broadcastDeleteTasksToDevice(mqttClient, t); + } catch (Exception ex) { + logger.error("Error push delete task: [{}, {}, {}]", t.getTerminalSN(), t.getId(), t.getLogId(), ex); + } + }); + + logger.debug("Get all pending download tasks .."); + List pendingDownloadTaskList = null; + if(checkOnline) { + pendingDownloadTaskList = downloadTaskDao.getPendingTaskForOnlineTerminals(periodeMinutes, limit); + } else { + pendingDownloadTaskList = downloadTaskDao.getPendingTaskForTerminals(periodeMinutes, limit); + } + logger.debug("... got {} task item.", pendingDownloadTaskList.size()); + pendingDownloadTaskList.forEach((t) -> { + try { + broadcastDownloadTasksToDevice(mqttClient, t); + } catch (Exception ex) { + logger.error("Error push download task: [{}, {}, {}]", t.getTerminalSN(), t.getId(), t.getLogId(), ex); + } + }); + } + + protected void publishBroadcastPendingTasksForAll() { + // pending delete task + Date currentTime = downloadTaskDao.getCurrentTime(); + logger.debug("Current server time: {}", currentTime); + logger.debug("Get all pending download tasks .."); + // pending download task + logger.debug("Get next BROADCAST pending download tasks .."); + PendingDownloadTask nextBroadcastDownloadTask = downloadTaskDao.getNextPendingTaskForBroadcast(); + if(nextBroadcastDownloadTask != null) { + logger.debug("Pending BROADCAST download task: \"{}\"", nextBroadcastDownloadTask.getName()); + try { + // cache last broadcasted task + GenericCache taskCache = GenericCache.getCache("LAST_BROADCAST_DOWNLOAD_TASK"); + taskCache.put("task_id", nextBroadcastDownloadTask.getId()); + + broadcastDownloadTasksToDevice(mqttClient, nextBroadcastDownloadTask); + } catch(Exception ex) { + logger.error("Error BROADCAST download task: [{}]", nextBroadcastDownloadTask.getId()); + } + } + } + + // send delete task to device topic via mqtt + private void broadcastDeleteTasksToDevice(MqttClient mqttClient, + PendingDeleteTask task) throws Exception { + logger.debug("Broadcast tasks ..."); + try { + int qos = 2; + String topicName = task.getTerminalSN().toUpperCase() + "_IN"; + // generate json + Gson gson = new Gson(); + Map messageMap = new HashMap<>(); + messageMap.put("req_id", UUID.randomUUID().toString()); + messageMap.put("req_time", dateTimeFormat.format(new Date())); + // we'll use log's id as ack_id + messageMap.put("ack_id", task.getLogId()); + messageMap.put("req_type", "DELETE_APP_TASK"); + // task map + Map taskMap = new HashMap<>(); + taskMap.put("id", task.getId()); + taskMap.put("name", task.getName()); + taskMap.put("delete_time", task.getDeleteTime() != null ? dateTimeFormat.format(task.getDeleteTime()) : null); + messageMap.put("task", taskMap); + // app map + ApplicationSimple app = task.getApp(); + Map appMap = new HashMap<>(); + appMap.put("id", app.getId()); + appMap.put("name", app.getAppName()); + appMap.put("package_name", app.getPackageName()); + messageMap.put("app", appMap); + + String messageData = gson.toJson(messageMap); + + logger.debug("Try publish message to: {}", topicName); + mqttClient.publish(topicName, messageData.getBytes(), qos, false); + logger.debug("Message published!"); + + // change log's last broadcast time + boolean updated = deleteTaskDao.saveLastBroadcastTs(task); + logger.debug("Delete task log last broadcast time updated? {}", updated); + } finally { + logger.debug("Start broadcast delete tasks DONE"); + } + } + + // send download task to device topic via mqtt + protected void broadcastDownloadTasksToDevice(MqttClient mqttClient, + PendingDownloadTask task) throws Exception { + logger.debug("Broadcast tasks ..."); + try { + int qos = 2; + String topicName; + if(!"SN".equals(task.getTerminalSN().toUpperCase())) { + topicName = task.getTerminalSN().toUpperCase() + "_IN"; + } else { + topicName = "SERVER_OUT"; + } + // generate json + Gson gson = new Gson(); + Map messageMap = new HashMap<>(); + messageMap.put("req_id", UUID.randomUUID().toString()); + messageMap.put("req_time", dateTimeFormat.format(new Date())); + // we'll use log's id as ack_id + messageMap.put("ack_id", task.getLogId()); + messageMap.put("req_type", "DOWNLOAD_APP_TASK"); + // task map + Map taskMap = new HashMap<>(); + taskMap.put("id", task.getId()); + taskMap.put("name", task.getName()); + taskMap.put("download_time_type", getDownloadTimeTypeName(task.getDownloadTimeType())); + if (task.getDownloadTimeType() == 2) { + taskMap.put("download_time", dateTimeFormat.format(task.getDownloadTime())); + } + taskMap.put("installation_time_type", getInstallationTimeTypeName(task.getInstallationTimeType())); + if (task.getInstallationTimeType() == 2) { + taskMap.put("installation_time", dateTimeFormat.format(task.getInstallationTime())); + } + taskMap.put("installation_notification", getInstallationNotificationName(task.getInstallationNotification())); + messageMap.put("task", taskMap); + // app map + Application app = task.getApp(); + Map appMap = new HashMap<>(); + appMap.put("id", app.getId()); + appMap.put("name", app.getAppName()); + appMap.put("package_name", app.getPackageName()); + appMap.put("version", app.getVersion()); + appMap.put("company", app.getCompanyName()); + appMap.put("uninstallable", app.isUninstallable()); + appMap.put("description", app.getDescription()); + if (app.getDownloadUrl() != null) { + appMap.put("download_url", app.getDownloadUrl()); + appMap.put("md5_checksum", app.getChecksum()); + } else { + String downloadURL = AppConfig.getInstance().get("file.download.url"); + if (downloadURL != null) { + downloadURL = downloadURL.replace("{$id}", app.getApkId()); + } + appMap.put("download_url", downloadURL); + String filePath = getFilePath(app.getFileId(), app.getFileExt(), app.getFileCreateDate()); + appMap.put("md5_checksum", md5Checksum(IOUtil.read(new FileInputStream(filePath)))); + } + messageMap.put("app", appMap); + + String messageData = gson.toJson(messageMap); + + logger.debug("Try publish message to: {}", topicName); + mqttClient.publish(topicName, messageData.getBytes(), qos, false); + logger.debug("Message published!"); + + // change log's last broadcast time + if(!"BROADCAST".equals(task.getLogId())) { + boolean updated = downloadTaskDao.saveLastBroadcastTs(task); + logger.debug("Download task log last broadcast time updated? {}", updated); + } else { + logger.debug("Do not update last broadcast time because this is broadcasted task!"); + } + } finally { + logger.debug("Start broadcast download tasks DONE"); + } + } + + // send NONE ack for invalid download task-SN to device topic via mqtt + protected void broadcastNoneDownloadTasksToDevice(MqttClient mqttClient, + String sn, String taskId) throws Exception { + logger.debug("Broadcast NONE tasks ..."); + try { + int qos = 2; + String topicName = sn.toUpperCase() + "_IN"; + // generate json + Gson gson = new Gson(); + Map messageMap = new HashMap<>(); + messageMap.put("req_id", UUID.randomUUID().toString()); + messageMap.put("req_type", "NONE"); + messageMap.put("req_time", dateTimeFormat.format(new Date())); + messageMap.put("task_id", taskId); + + String messageData = gson.toJson(messageMap); + + logger.debug("Try publish message to: {}", topicName); + mqttClient.publish(topicName, messageData.getBytes(), qos, false); + logger.debug("Message published!"); + } finally { + logger.debug("Start broadcast NONE download tasks DONE"); + } + } + + protected void publishBroadcastCheckPendingTasksForAll() { + // pending check task + Date currentTime = downloadTaskDao.getCurrentTime(); + logger.debug("Current server time: {}", currentTime); + logger.debug("Get all pending check tasks .."); + List tasks = downloadTaskDao.getPendingTasksForBroadcast(); + if(!tasks.isEmpty()) { + tasks.forEach((task) -> { + try { + logger.debug("Pending BROADCAST check task: \"{} - {}\"", task.getId(), task.getName()); + broadcastCheckTaskToDevice(mqttClient, task); + } catch(Exception ex) { + logger.error("Error BROADCAST check task: [{} - {}]", task.getId(), task.getName()); + } + }); + } + } + + // broadcast check task to all device topic via mqtt + private void broadcastCheckTaskToDevice(MqttClient mqttClient, + SimplePendingDownloadTask task) throws Exception { + logger.debug("Broadcast tasks ..."); + try { + int qos = 2; + String topicName = "SERVER_OUT"; + // generate json + Gson gson = new Gson(); + Map messageMap = new HashMap<>(); + messageMap.put("req_id", UUID.randomUUID().toString()); + messageMap.put("req_time", dateTimeFormat.format(new Date())); + // we'll put task id here as key so same task will be ignored by device if received + // more than 1 time + messageMap.put("task_id", task.getId()); + messageMap.put("req_type", "CHECK_TASK"); + messageMap.put("group_list", task.getGroupIds()); + + String messageData = gson.toJson(messageMap); + + logger.debug("Try publish message to: {}", topicName); + mqttClient.publish(topicName, messageData.getBytes(), qos, false); + logger.debug("Message published!"); + } finally { + logger.debug("Start broadcast check tasks DONE"); + } + } + + private String getDownloadTimeTypeName(int type) { + switch (type) { + case 1: + return "NEXT_CONTACT"; + case 2: + return "DATETIME"; + } + return null; + } + + private String getInstallationTimeTypeName(int type) { + switch (type) { + case 1: + return "IMMEDIATE_AFTER_DOWNLOAD"; + case 2: + return "DATETIME"; + } + return null; + } + + private String getInstallationNotificationName(int type) { + switch (type) { + case 1: + return "SILENT"; + case 2: + return "NEED_CONFIRMATION"; + } + return null; + } + + private String getFilePath(String fileId, String fileExt, Date fileCreateDate) { + String filePath = AppConfig.getInstance().get("file.dir") + File.separator + + sdfYEAR.format(fileCreateDate) + File.separator + + sdfMONTH.format(fileCreateDate) + File.separator + + sdfDATE.format(fileCreateDate) + File.separator + + fileId + "." + fileExt; + return filePath; + } + + private String md5Checksum(byte[] bytes) throws Exception { + try { + String checksum = DigestUtils.md5Hex(bytes); + return checksum; + } finally { + } + }*/ +} diff --git a/src/id/iptek/utms/agent/worker/WorkerFactory.java b/src/id/iptek/utms/agent/worker/WorkerFactory.java new file mode 100644 index 0000000..dc02ca0 --- /dev/null +++ b/src/id/iptek/utms/agent/worker/WorkerFactory.java @@ -0,0 +1,51 @@ +package id.iptek.utms.agent.worker; + +import id.iptek.utms.agent.AppConfig; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jaka + */ +public final class WorkerFactory { + + private static WorkerFactory self; + private static final Logger logger = LoggerFactory.getLogger(WorkerFactory.class); + + private WorkerFactory() { + } + + public List buildWorkers(AppConfig config) throws Exception { + List workers = new ArrayList<>(); + try { + String[] consumerNames = config.getAll("mqtt.consumers"); + + logger.info("Build workers ..."); + for(String consumerName : consumerNames) { + logger.debug("Build worker: '{}'", consumerName); + String workerClass = config.get("mqtt.consumer." + consumerName + ".workerclass"); + Class clazz = Class.forName(workerClass); + int _threads = config.getAsInt("mqtt.consumer." + consumerName + ".threads", 1); + + for(int a = 0; a < _threads; a++) { + Worker worker = (Worker) clazz.newInstance(); + worker.init(consumerName, config); + workers.add(worker); + } + } + logger.info("... done build workers."); + } finally { + } + return workers; + } + + public static WorkerFactory getInstance() { + if(self == null) { + self = new WorkerFactory(); + } + return self; + } +} diff --git a/utms-agent.service b/utms-agent.service new file mode 100644 index 0000000..d8c41bd --- /dev/null +++ b/utms-agent.service @@ -0,0 +1,15 @@ +[Unit] +Description=UTMS Agent +After=network.target + +[Service] +Type=simple +User=tomcat +Group=tomcat +WorkingDirectory=/opt/utms-agent +ExecStart=/usr/bin/java -jar /opt/utms-agent/utms-agent.jar /opt/utms-agent/conf/mqtt-agents.cfg +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target \ No newline at end of file