initial import
This commit is contained in:
48
modules/core/src/com/cmobile/unifiedtms/MqttConfig.java
Normal file
48
modules/core/src/com/cmobile/unifiedtms/MqttConfig.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.cmobile.unifiedtms;
|
||||
|
||||
import com.haulmont.cuba.core.config.Config;
|
||||
import com.haulmont.cuba.core.config.Property;
|
||||
import com.haulmont.cuba.core.config.Source;
|
||||
import com.haulmont.cuba.core.config.SourceType;
|
||||
import com.haulmont.cuba.core.config.defaults.Default;
|
||||
import com.haulmont.cuba.core.global.Secret;
|
||||
|
||||
@Source(type = SourceType.DATABASE)
|
||||
public interface MqttConfig extends Config {
|
||||
|
||||
@Property("mqtt.enabled")
|
||||
Boolean getMqttEnabled();
|
||||
|
||||
@Property("mqtt.brokerUrl")
|
||||
String getBrokerURL();
|
||||
|
||||
@Property("mqtt.clientId")
|
||||
String getClientId();
|
||||
|
||||
@Property("mqtt.username")
|
||||
String getUsername();
|
||||
|
||||
@Property("mqtt.password")
|
||||
@Secret
|
||||
String getPassword();
|
||||
|
||||
@Property("mqtt.incomingMessageTopic")
|
||||
@Default(value = "SERVER_IN")
|
||||
String getIncomingMessageTopic();
|
||||
|
||||
@Property("mqtt.autoReconnect")
|
||||
@Default(value = "true")
|
||||
Boolean getAutoReconnect();
|
||||
|
||||
@Property("mqtt.keepAliveInterval")
|
||||
@Default(value = "10000")
|
||||
Integer getKeepAliveInterval();
|
||||
|
||||
@Property("mqtt.disconnectTimeout")
|
||||
@Default(value = "5000")
|
||||
Long getDisconnectTimeout();
|
||||
|
||||
@Property("mqtt.device-host")
|
||||
String getDeviceHost();
|
||||
|
||||
}
|
||||
30
modules/core/src/com/cmobile/unifiedtms/Singleton.java
Normal file
30
modules/core/src/com/cmobile/unifiedtms/Singleton.java
Normal file
@ -0,0 +1,30 @@
|
||||
package com.cmobile.unifiedtms;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Singleton {
|
||||
|
||||
private final static Map<String, Singleton> 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;
|
||||
}
|
||||
}
|
||||
46
modules/core/src/com/cmobile/unifiedtms/app.properties
Normal file
46
modules/core/src/com/cmobile/unifiedtms/app.properties
Normal file
@ -0,0 +1,46 @@
|
||||
###############################################################################
|
||||
# Configuration #
|
||||
###############################################################################
|
||||
|
||||
cuba.dbmsType = postgres
|
||||
|
||||
cuba.springContextConfig = +com/cmobile/unifiedtms/spring.xml
|
||||
|
||||
cuba.persistenceConfig = +com/cmobile/unifiedtms/persistence.xml
|
||||
|
||||
cuba.metadataConfig = +com/cmobile/unifiedtms/metadata.xml
|
||||
|
||||
cuba.viewsConfig = +com/cmobile/unifiedtms/views.xml
|
||||
|
||||
cuba.mainMessagePack = +com.cmobile.unifiedtms.core
|
||||
|
||||
cuba.keyForSecurityTokenEncryption = 9WMPJQzNkeWDe90V
|
||||
|
||||
cuba.anonymousSessionId = 3aa2d4b1-205c-de8c-bcb6-f4153ae617c7
|
||||
|
||||
###############################################################################
|
||||
# Other #
|
||||
###############################################################################
|
||||
|
||||
cuba.webContextName = tms-core
|
||||
cuba.availableLocales = English|en;Indonesian (Indonesia)|id_ID
|
||||
cuba.localeSelectVisible = false
|
||||
|
||||
cuba.dataSourceProvider = application
|
||||
cuba.dataSource.username = utms
|
||||
cuba.dataSource.password = utms1234
|
||||
cuba.dataSource.dbName = utms
|
||||
cuba.dataSource.host = localhost
|
||||
cuba.dataSource.port =
|
||||
|
||||
# Multi Tenancy
|
||||
cubasdbmt.loginByTenantParamEnabled = true
|
||||
cubasdbmt.tenantIdUrlParamName = tenantId
|
||||
cuba.ftsConfig=+com/cmobile/unifiedtms/fts.xml
|
||||
|
||||
#license.concurrentSessionsLimit = 1
|
||||
#cuba.legacyPasswordEncryptionModule = cuba_Md5EncryptionModule
|
||||
#cuba.passwordEncryptionModule = cuba_Md5EncryptionModule
|
||||
|
||||
#whitelist.package.names = (com.vfi.*|com.briit.*|com.unified.*|com.klopos.*|id.co.bri.*)
|
||||
#blacklist.threshold = 5
|
||||
@ -0,0 +1,16 @@
|
||||
package com.cmobile.unifiedtms.config;
|
||||
|
||||
import com.haulmont.cuba.core.config.Config;
|
||||
import com.haulmont.cuba.core.config.Property;
|
||||
import com.haulmont.cuba.core.config.Source;
|
||||
import com.haulmont.cuba.core.config.SourceType;
|
||||
import com.haulmont.cuba.core.config.defaults.Default;
|
||||
|
||||
@Source(type = SourceType.DATABASE)
|
||||
public interface LicenseConfig extends Config {
|
||||
|
||||
@Property("unified.license.string")
|
||||
String getLicenseString();
|
||||
|
||||
void setLicenseString(String licenseString);
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.cmobile.unifiedtms.core.config;
|
||||
|
||||
import com.haulmont.cuba.core.config.Config;
|
||||
import com.haulmont.cuba.core.config.Property;
|
||||
import com.haulmont.cuba.core.config.Source;
|
||||
import com.haulmont.cuba.core.config.SourceType;
|
||||
import com.haulmont.cuba.core.config.defaults.Default;
|
||||
|
||||
@Source(type = SourceType.DATABASE)
|
||||
public interface EchoTestConfig extends Config {
|
||||
|
||||
@Property("mqtt.echo.missing.max")
|
||||
@Default("3")
|
||||
Integer getEchoMissingMax();
|
||||
|
||||
@Property("mqtt.echo.missing.after")
|
||||
@Default("10000")
|
||||
Long getEchoMissingAfter();
|
||||
|
||||
@Property("mqtt.echo.sleep")
|
||||
@Default("1000")
|
||||
Long getEchoCheckSleep();
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.cmobile.unifiedtms.core.config;
|
||||
|
||||
import com.haulmont.cuba.core.config.Config;
|
||||
import com.haulmont.cuba.core.config.Property;
|
||||
import com.haulmont.cuba.core.config.Source;
|
||||
import com.haulmont.cuba.core.config.SourceType;
|
||||
import com.haulmont.cuba.core.config.defaults.Default;
|
||||
import com.haulmont.cuba.core.config.defaults.DefaultInt;
|
||||
|
||||
@Source(type = SourceType.DATABASE)
|
||||
public interface ExtendedConfig extends Config {
|
||||
|
||||
@Property("whitelist.package.names")
|
||||
@Default("(.*)")
|
||||
String getWhitelistPackageNames();
|
||||
|
||||
@Property("blacklist.threshold")
|
||||
@DefaultInt(5)
|
||||
int getBlacklistThreshold();
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.cmobile.unifiedtms.core.config;
|
||||
|
||||
import com.haulmont.cuba.core.config.Config;
|
||||
import com.haulmont.cuba.core.config.Property;
|
||||
import com.haulmont.cuba.core.config.Source;
|
||||
import com.haulmont.cuba.core.config.SourceType;
|
||||
import com.haulmont.cuba.core.config.defaults.Default;
|
||||
|
||||
@Source(type = SourceType.DATABASE)
|
||||
public interface FileDownloadConfig extends Config {
|
||||
|
||||
@Property("unified.filedownload.url")
|
||||
@Default("https://unifiedtms.id/download/services/file/get?id={$id}")
|
||||
String getFileDownloadURL();
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.cmobile.unifiedtms.core.config;
|
||||
|
||||
import com.haulmont.cuba.core.config.Config;
|
||||
import com.haulmont.cuba.core.config.Property;
|
||||
import com.haulmont.cuba.core.config.Source;
|
||||
import com.haulmont.cuba.core.config.SourceType;
|
||||
import com.haulmont.cuba.core.config.defaults.DefaultBoolean;
|
||||
import com.haulmont.cuba.core.config.defaults.DefaultString;
|
||||
|
||||
@Source(type = SourceType.DATABASE)
|
||||
public interface TerminalUpdateConfig extends Config {
|
||||
|
||||
@Property("utms.force.terminal-update")
|
||||
@DefaultBoolean(false)
|
||||
Boolean getForceUpdate();
|
||||
|
||||
@Property("utms.force.terminal-not-update")
|
||||
@DefaultBoolean(false)
|
||||
Boolean getForceNotUpdate();
|
||||
|
||||
@Property("utms.force.terminal-update.mode")
|
||||
@DefaultString("NORMAL")
|
||||
String getForceNotUpdateMode();
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt;
|
||||
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
public class BlindTrustManager implements X509TrustManager {
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,265 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt;
|
||||
|
||||
import com.cmobile.unifiedtms.MqttConfig;
|
||||
import com.cmobile.unifiedtms.core.config.EchoTestConfig;
|
||||
import com.cmobile.unifiedtms.core.config.FileDownloadConfig;
|
||||
import com.cmobile.unifiedtms.core.mqtt.listener.*;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Component(MqttConnector.NAME)
|
||||
public class MqttConnector<LoginService> {
|
||||
public static final String NAME = "tms_MQTTConnector";
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private MqttConfig mqttConfig;
|
||||
@Inject
|
||||
private Authentication authentication;
|
||||
@Inject
|
||||
private FileDownloadConfig fileDownloadConfig;
|
||||
@Inject
|
||||
private EchoTestConfig echoTestConfig;
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
|
||||
private MqttClient mqttClient;
|
||||
private List<IncomingMessageListener> messageListeners = new ArrayList<>();
|
||||
private AtomicBoolean echoTesterStarted = new AtomicBoolean(false);
|
||||
private ExecutorService exec = Executors.newSingleThreadExecutor();
|
||||
private Date lastEchoReceivedTime = null;
|
||||
private Date echoCheckStartedTime = null;
|
||||
private AtomicInteger echoMissing = new AtomicInteger(0);
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
logger.debug("#postConstruct");
|
||||
try {
|
||||
logger.debug("-> mqtt enabled? {}", mqttConfig.getMqttEnabled());
|
||||
if(mqttConfig.getMqttEnabled() != null && mqttConfig.getMqttEnabled()) {
|
||||
connectAndListen();
|
||||
addDefaultListeners();
|
||||
//startEchoTestMaintainer();
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Cannot init & connecting MQTT Client: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private MqttClient initClient() throws Exception {
|
||||
logger.debug("Initialize MQTT Client ...");
|
||||
try {
|
||||
MemoryPersistence persistence = new MemoryPersistence();
|
||||
MqttClient _mqttClient = new MqttClient(mqttConfig.getBrokerURL(),
|
||||
//mqttConfig.getClientId(),
|
||||
MqttAsyncClient.generateClientId(),
|
||||
persistence);
|
||||
_mqttClient.setCallback(new MqttCallbackExtended() {
|
||||
@Override
|
||||
public void connectComplete(boolean bln, String string) {
|
||||
//System.out.println("Connect complete? " + bln + " : " + string);
|
||||
try {
|
||||
_mqttClient.subscribe(mqttConfig.getIncomingMessageTopic());
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error subscribe topic: " + mqttConfig.getIncomingMessageTopic());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionLost(Throwable thrwbl) {
|
||||
logger.error("Connection Lost: " + thrwbl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageArrived(String topic, MqttMessage mm) throws Exception {
|
||||
logger.debug("<< " + new String(mm.getPayload()) + " (" + mm.getId() + " - " + mm.getQos() + ")");
|
||||
String messageString = new String(mm.getPayload());
|
||||
for(IncomingMessageListener listener : messageListeners) {
|
||||
boolean handled = listener.incomingMessage(MqttConnector.this, topic, messageString);
|
||||
if(handled) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//mm.setRetained(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliveryComplete(IMqttDeliveryToken imdt) {
|
||||
System.err.println("Delivery Complete: " + imdt.getMessageId());
|
||||
}
|
||||
|
||||
});
|
||||
logger.debug("MQTT Client Initialized.");
|
||||
return _mqttClient;
|
||||
} catch (MqttException me) {
|
||||
throw me;
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
private void connectAndListen() throws Exception {
|
||||
logger.debug("Connect to Broker and Listen for incoming messages ...");
|
||||
if(mqttClient == null) {
|
||||
mqttClient = initClient();
|
||||
}
|
||||
MqttConnectOptions connOpts = new MqttConnectOptions();
|
||||
connOpts.setCleanSession(true);
|
||||
connOpts.setUserName(mqttConfig.getUsername());
|
||||
connOpts.setPassword(mqttConfig.getPassword().toCharArray());
|
||||
connOpts.setAutomaticReconnect(mqttConfig.getAutoReconnect());
|
||||
connOpts.setKeepAliveInterval(mqttConfig.getKeepAliveInterval());
|
||||
if(mqttConfig.getBrokerURL().toLowerCase().contains("ssl://")) {
|
||||
connOpts.setSocketFactory(buildSocketFactory());
|
||||
}
|
||||
mqttClient.connect(connOpts);
|
||||
}
|
||||
|
||||
private void addDefaultListeners() throws Exception {
|
||||
// pending task push listener will always return false so it will continue
|
||||
// to other listeners
|
||||
//PendingTaskPushListener ptpl = new PendingTaskPushListener(authentication);
|
||||
//ptpl.setFileDownloadConfig(fileDownloadConfig);
|
||||
//addIncomingMessageListener(ptpl);
|
||||
addIncomingMessageListener(new HeartBeatListener(authentication));
|
||||
addIncomingMessageListener(new DiagnosticInfoListener(authentication));
|
||||
DownloadTaskAckListener dtl = new DownloadTaskAckListener(authentication);
|
||||
dtl.setPersistence(persistence);
|
||||
addIncomingMessageListener(dtl);
|
||||
DeleteTaskAckListener deltl = new DeleteTaskAckListener(authentication);
|
||||
deltl.setPersistence(persistence);
|
||||
addIncomingMessageListener(deltl);
|
||||
addIncomingMessageListener(new DeviceInitListener(authentication));
|
||||
addIncomingMessageListener(new EchoListener());
|
||||
}
|
||||
|
||||
private void startEchoTestMaintainer() throws Exception {
|
||||
logger.debug("Start Echo Test Maintainer ...");
|
||||
Runnable run = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean set = echoTesterStarted.compareAndSet(false, true);
|
||||
echoCheckStartedTime = new Date();
|
||||
logger.debug("Set? {}", set);
|
||||
logger.debug("Echo Check Start ...");
|
||||
while(echoTesterStarted.get()) {
|
||||
logger.debug("Echo Check ...");
|
||||
if(mqttClient != null) {
|
||||
if(lastEchoReceivedTime == null) {
|
||||
lastEchoReceivedTime = echoCheckStartedTime;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
long echoMissingAfter = echoTestConfig.getEchoMissingAfter();
|
||||
long timeSentMS = lastEchoReceivedTime.getTime();
|
||||
long nowMS = now.getTime();
|
||||
long diff = nowMS - timeSentMS;
|
||||
boolean expired = diff >= echoMissingAfter;
|
||||
logger.debug("Last Echo received: {} - {} ; Expired after {}? {}", lastEchoReceivedTime, now, echoMissingAfter, expired);
|
||||
if(expired) {
|
||||
int maxEchoMissing = echoTestConfig.getEchoMissingMax();
|
||||
int echoMissingCount = echoMissing.incrementAndGet();
|
||||
logger.debug("Missed Echo: {} x {}", echoMissingCount, maxEchoMissing);
|
||||
// restart mqttclient if max reached
|
||||
if(echoMissingCount >= maxEchoMissing) {
|
||||
try {
|
||||
logger.debug("Forcibly disconnect Mqtt ...");
|
||||
mqttClient.disconnectForcibly();
|
||||
mqttClient.close(true);
|
||||
mqttClient = null;
|
||||
lastEchoReceivedTime = null;
|
||||
echoMissing.set(0);
|
||||
|
||||
// sleep for 1000 ms
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
|
||||
logger.debug("... then re-connect!");
|
||||
connectAndListen();
|
||||
echoCheckStartedTime = new Date();
|
||||
} catch (Exception ex) {
|
||||
logger.error("Error restart Mqtt Client: {}", ex.getMessage(), ex);
|
||||
}
|
||||
} else {
|
||||
lastEchoReceivedTime = new Date();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Maybe still re-connecting: no check!");
|
||||
}
|
||||
try {
|
||||
long sleep = echoTestConfig.getEchoCheckSleep();
|
||||
logger.debug("Sleep for {} ms", sleep);
|
||||
TimeUnit.MILLISECONDS.sleep(sleep);
|
||||
} catch(InterruptedException ex) {
|
||||
logger.error("Echo Test Check got interrupted!!", ex);
|
||||
}
|
||||
}
|
||||
logger.debug("Echo Check Ended ...");
|
||||
}
|
||||
};
|
||||
exec.submit(run);
|
||||
}
|
||||
|
||||
public void echoReceived(Date timeSent) {
|
||||
this.lastEchoReceivedTime = timeSent;
|
||||
}
|
||||
|
||||
public void addIncomingMessageListener(IncomingMessageListener listener) {
|
||||
messageListeners.add(listener);
|
||||
}
|
||||
|
||||
public void disconnect() throws Exception {
|
||||
logger.debug("Disconnect before waiting for {} ms", mqttConfig.getDisconnectTimeout());
|
||||
if(mqttClient != null) {
|
||||
mqttClient.disconnectForcibly(mqttConfig.getDisconnectTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
public void publishMessage(String topic, String messageData, int qos) throws Exception {
|
||||
publishMessage(topic, messageData.getBytes(), qos, false);
|
||||
}
|
||||
|
||||
public void publishRetainedMessage(String topic, String messageData, int qos) throws Exception {
|
||||
publishMessage(topic, messageData.getBytes(), qos, true);
|
||||
}
|
||||
|
||||
public void publishMessage(String topic, byte[] bytes, int qos, boolean retained) throws Exception {
|
||||
if(mqttClient == null) {
|
||||
mqttClient = initClient();
|
||||
connectAndListen();
|
||||
} else if(!mqttClient.isConnected()) {
|
||||
connectAndListen();
|
||||
}
|
||||
MqttMessage message = new MqttMessage(bytes);
|
||||
message.setRetained(retained);
|
||||
message.setQos(qos);
|
||||
logger.debug(">> {}", new String(bytes));
|
||||
mqttClient.publish(topic, message);
|
||||
}
|
||||
|
||||
private SSLSocketFactory buildSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
|
||||
SSLContext ctx = SSLContext.getInstance("SSL");
|
||||
ctx.init(null, new TrustManager[] { new BlindTrustManager() }, null);
|
||||
return ctx.getSocketFactory();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,219 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.DeleteTask;
|
||||
import com.cmobile.unifiedtms.entity.DeleteTaskLog;
|
||||
import com.cmobile.unifiedtms.entity.DownloadTask;
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.enums.DeleteTaskActivity;
|
||||
import com.cmobile.unifiedtms.entity.enums.TaskStatus;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class DeleteTaskAckListener implements IncomingMessageListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(DeleteTaskAckListener.class);
|
||||
private Authentication authentication;
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
private Persistence persistence;
|
||||
|
||||
public DeleteTaskAckListener(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public void setPersistence(Persistence persistence) {
|
||||
this.persistence = persistence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message) {
|
||||
try {
|
||||
String json = message;
|
||||
Gson gson = new Gson();
|
||||
Map map = gson.fromJson(json, Map.class);
|
||||
logger.debug("Incoming MAP: " + map);
|
||||
|
||||
logger.debug("Before Authentication ...");
|
||||
authentication.begin();
|
||||
logger.debug("Authenticated");
|
||||
|
||||
String type = (String) map.get("req_type");
|
||||
if("ACK_DELETE_APP_TASK".equals(type)) {
|
||||
logger.debug("Detected Incoming: Ack of Delete Task ...");
|
||||
|
||||
UUID reqId = UUID.fromString((String) map.get("req_id"));
|
||||
reqId = UUID.randomUUID();
|
||||
String terminalSN = (String) map.get("device_sn");
|
||||
Terminal terminal = loadTerminalBySN(terminalSN);
|
||||
UUID ackId = UUID.fromString((String) map.get("ack_id"));
|
||||
DeleteTaskLog log = loadDeleteTaskLog(ackId);
|
||||
|
||||
if(terminal != null) {
|
||||
if(log != null) {
|
||||
String status = (String) map.get("status");
|
||||
if("ACCEPTED".equals(status)) {
|
||||
status = "NOT_STARTED";
|
||||
}
|
||||
DeleteTaskActivity activity = DeleteTaskActivity.valueOf(status);
|
||||
log.setActivity(activity);
|
||||
|
||||
boolean saved = commitToDB(log);
|
||||
logger.debug("Save Ack to DB: " + saved);
|
||||
if(saved) {
|
||||
if("DELETE_SUCCESS".equals(status) ||
|
||||
"DELETE_FAIL".equals(status)) {
|
||||
// send ack DELETE_TASK_CONFIRM_ACK
|
||||
broadcastDeleteTasksConfirmACK(connector, terminalSN, ackId);
|
||||
}
|
||||
|
||||
int totalUnfinishedTask = getUnfinishedTask(log.getTask().getId());
|
||||
if(totalUnfinishedTask == 0) {
|
||||
// mark task as complete
|
||||
DeleteTask task = log.getTask();
|
||||
task.setStatus(TaskStatus.DONE);
|
||||
boolean taskCommitted = commitToDB(task);
|
||||
logger.debug("Save Task to DB: " + taskCommitted);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("Invalid Delete Task Log (will give successful ack): {}", ackId);
|
||||
// send ack DELETE_TASK_CONFIRM_ACK
|
||||
broadcastDeleteTasksConfirmACK(connector, terminalSN, ackId);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Invalid Terminal: {}", terminalSN);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// not my part
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error handling incoming message: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
authentication.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Integer getUnfinishedTask(UUID downloadTaskId) {
|
||||
Double value = _getValueFromQuery("select count(*) from public.tms_delete_task_log where task_id = '" + downloadTaskId.toString() + "' and activity not in (3,4);");
|
||||
logger.debug("--> value: '" + value + "'");
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Double _getValueFromQuery(String query) {
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
Set<Double> numbers = runner.query(query,
|
||||
new ResultSetHandler<Set<Double>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<Double> handle(ResultSet rs) throws SQLException {
|
||||
Set<Double> rows = new HashSet<Double>();
|
||||
while (rs.next()) {
|
||||
rows.add(rs.getDouble(1));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
});
|
||||
return numbers.isEmpty() ? null : numbers.toArray(new Double[] {})[0];
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// send download task to device topic via mqtt
|
||||
public void broadcastDeleteTasksConfirmACK(MqttConnector mqttConnector,
|
||||
String terminalSN,
|
||||
UUID 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.toString());
|
||||
messageMap.put("req_type", "DELETE_TASK_CONFIRM_ACK");
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.debug("Try publish message to: {}", topicName);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
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 int getInt(Map map, String key) {
|
||||
Object val = map.get(key);
|
||||
if(val instanceof String) {
|
||||
return Integer.parseInt((String) val);
|
||||
} else if(val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
} else if(val instanceof Double) {
|
||||
return ((Double) val).intValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String getString(Map map, String key) {
|
||||
return (String) map.get(key);
|
||||
}
|
||||
|
||||
private boolean commitToDB(DeleteTaskLog log) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
DeleteTaskLog committed = dataManager.commit(log);
|
||||
//return PersistenceHelper.isManaged(committed);
|
||||
return committed != null;
|
||||
}
|
||||
|
||||
private boolean commitToDB(DeleteTask task) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
DeleteTask committed = dataManager.commit(task);
|
||||
//return PersistenceHelper.isManaged(committed);
|
||||
return committed != null;
|
||||
}
|
||||
|
||||
private Terminal loadTerminalBySN(String terminalSN) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where lower(t.sn)=:sn and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("sn", terminalSN.toLowerCase());
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
|
||||
private DeleteTaskLog loadDeleteTaskLog(UUID logId) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<DeleteTaskLog> loadContext = LoadContext.create(DeleteTaskLog.class);
|
||||
loadContext.setQueryString("select t from tms_DeleteTaskLog t where t.id=:id");
|
||||
loadContext.getQuery().setParameter("id", logId);
|
||||
loadContext.setView("deleteTaskLog-view");
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.DeviceProfile;
|
||||
import com.cmobile.unifiedtms.entity.HeartBeat;
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.enums.DownloadTaskType;
|
||||
import com.cmobile.unifiedtms.entity.enums.DownloadTimeType;
|
||||
import com.cmobile.unifiedtms.entity.enums.InstallationTimeType;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.core.global.PersistenceHelper;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DeviceInitListener implements IncomingMessageListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(DeviceInitListener.class);
|
||||
private Authentication authentication;
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
|
||||
|
||||
public DeviceInitListener(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message) {
|
||||
try {
|
||||
String json = message;
|
||||
Gson gson = new Gson();
|
||||
Map map = gson.fromJson(json, Map.class);
|
||||
logger.debug("Incoming MAP: " + map);
|
||||
|
||||
logger.debug("Before Authentication ...");
|
||||
authentication.begin();
|
||||
logger.debug("Authenticated");
|
||||
|
||||
String type = (String) map.get("req_type");
|
||||
if("INIT".equals(type)) {
|
||||
logger.debug("Detected Incoming: Init ...");
|
||||
|
||||
UUID reqId = UUID.fromString((String) map.get("req_id"));
|
||||
reqId = UUID.randomUUID();
|
||||
String terminalSN = (String) map.get("device_sn");
|
||||
|
||||
Terminal terminal = loadTerminalBySN(terminalSN);
|
||||
if(terminal != null) {
|
||||
// get terminal's profile
|
||||
DeviceProfile profile = terminal.getProfile();
|
||||
if(profile != null) {
|
||||
|
||||
int qos = 2;
|
||||
String topicName = terminal.getSn().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().toString());
|
||||
profileMap.put("name", profile.getName());
|
||||
profileMap.put("hearbeat_interval", profile.getHeartbeatInterval());
|
||||
profileMap.put("diagnostic_interval", profile.getDiagnosticInterval());
|
||||
profileMap.put("mask_home_button", profile.getMaskHomeButton());
|
||||
profileMap.put("mask_status_button", profile.getMaskStatusBar());
|
||||
profileMap.put("schedule_reboot", profile.getScheduleReboot());
|
||||
if (profile.getScheduleReboot()) {
|
||||
profileMap.put("schedule_reboot_time", timeFormat.format(profile.getScheduleRebootTime()));
|
||||
}
|
||||
profileMap.put("relocation_alert", profile.getRelocationAlert());
|
||||
profileMap.put("moving_threshold", profile.getMovingThreshold());
|
||||
profileMap.put("admin_password", profile.getAdminPassword());
|
||||
messageMap.put("profile", profileMap);
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.debug("Try publish message to: {}", topicName);
|
||||
connector.publishRetainedMessage(topicName, messageData, qos);
|
||||
logger.debug("Message published!");
|
||||
} else {
|
||||
logger.debug("Device has no profile: {}", terminal.getSn());
|
||||
}
|
||||
} else {
|
||||
logger.debug("Invalid Terminal: {}", terminalSN);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// not my part
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error handling incoming message: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
authentication.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getInt(Map map, String key) {
|
||||
Object val = map.get(key);
|
||||
if(val instanceof String) {
|
||||
return Integer.parseInt((String) val);
|
||||
} else if(val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
} else if(val instanceof Double) {
|
||||
return ((Double) val).intValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean commitToDB(HeartBeat heartBeat) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
HeartBeat committed = dataManager.commit(heartBeat);
|
||||
return PersistenceHelper.isManaged(committed);
|
||||
}
|
||||
|
||||
private Terminal loadTerminalBySN(String terminalSN) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where lower(t.sn)=:sn and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("sn", terminalSN.toLowerCase());
|
||||
loadContext.setView("terminal-with-profile-view");
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.ApplicationSimple;
|
||||
import com.cmobile.unifiedtms.entity.DiagnosticInfo;
|
||||
import com.cmobile.unifiedtms.entity.HeartBeat;
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
|
||||
public class DiagnosticInfoListener implements IncomingMessageListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(DiagnosticInfoListener.class);
|
||||
private Authentication authentication;
|
||||
|
||||
public DiagnosticInfoListener(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message) {
|
||||
try {
|
||||
String json = message;
|
||||
Gson gson = new Gson();
|
||||
Map map = gson.fromJson(json, Map.class);
|
||||
logger.info("Incoming MAP: " + map);
|
||||
|
||||
logger.info("Before Authentication ...");
|
||||
authentication.begin();
|
||||
logger.info("Authenticated");
|
||||
|
||||
String type = (String) map.get("req_type");
|
||||
if("DIAGNOSTIC".equals(type)) {
|
||||
logger.info("Detected Incoming: Diagnostic Info ...");
|
||||
|
||||
UUID reqId = UUID.fromString((String) map.get("req_id"));
|
||||
reqId = UUID.randomUUID();
|
||||
String terminalSN = (String) map.get("device_sn");
|
||||
|
||||
DiagnosticInfo diagnosticInfo = new DiagnosticInfo();
|
||||
diagnosticInfo.setId(reqId);
|
||||
diagnosticInfo.setCreateTs(new Date());
|
||||
diagnosticInfo.setCreatedBy(terminalSN);
|
||||
diagnosticInfo.setVersion(1);
|
||||
Terminal terminal = loadTerminalBySN(terminalSN);
|
||||
if(terminal != null) {
|
||||
// diagnostic info - mandatory
|
||||
Map diagnosticInfoMap = (Map) map.get("diagnostic_info");
|
||||
boolean isNewDiagnostic = getBoolean(diagnosticInfoMap, "new_diagnostic");
|
||||
|
||||
diagnosticInfo.setTerminal(terminal);
|
||||
diagnosticInfo.setBatteryPercentage(getInt(diagnosticInfoMap, "battery_percentage"));
|
||||
double temp = getDouble(diagnosticInfoMap, "battery_temp");
|
||||
// handel temp for new diagnostic
|
||||
if(isNewDiagnostic) {
|
||||
temp /= 10;
|
||||
}
|
||||
diagnosticInfo.setBatteryTemp(temp);
|
||||
|
||||
diagnosticInfo.setMeid((String) diagnosticInfoMap.get("meid"));
|
||||
diagnosticInfo.setTotalMemory(getLong(diagnosticInfoMap, "total_memory"));
|
||||
diagnosticInfo.setAvailableMemory(getLong(diagnosticInfoMap, "available_memory"));
|
||||
diagnosticInfo.setTotalFlashMemory(getLong(diagnosticInfoMap, "total_flash_memory"));
|
||||
diagnosticInfo.setAvailableFlashMemory(getLong(diagnosticInfoMap, "available_flash_memory"));
|
||||
diagnosticInfo.setTotalMobileData(getLong(diagnosticInfoMap, "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"));
|
||||
|
||||
// installed apps
|
||||
List<Map<String, Object>> installedApps = (List<Map<String, Object>>) map.get("installed_apps");
|
||||
if(installedApps != null) {
|
||||
Metadata metadata = AppBeans.get(Metadata.NAME);
|
||||
List<ApplicationSimple> 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 = metadata.create(ApplicationSimple.class);
|
||||
appSimple.setAppName(appName);
|
||||
appSimple.setPackageName(packageName);
|
||||
appSimple.setAppVersion(version);
|
||||
appSimple.setDiagnosticInfo(diagnosticInfo);
|
||||
applications.add(appSimple);
|
||||
});
|
||||
diagnosticInfo.setInstalledApps(applications);
|
||||
String installedAppsJson = gson.toJson(installedApps);
|
||||
diagnosticInfo.setInstalledAppsString(installedAppsJson);
|
||||
}
|
||||
|
||||
// location info - mandatory
|
||||
Map locationInfoMap = (Map) map.get("location_info");
|
||||
diagnosticInfo.setLatitude((Double) locationInfoMap.get("lat"));
|
||||
diagnosticInfo.setLongitude((Double) locationInfoMap.get("lng"));
|
||||
boolean saved = commitToDB(diagnosticInfo);
|
||||
logger.info("Save Diagnostic Info to DB: " + saved);
|
||||
} else {
|
||||
logger.info("Invalid Terminal: {}", terminalSN);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// not my part
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error handling incoming message: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
authentication.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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());
|
||||
} else if(val instanceof Boolean) {
|
||||
return (Boolean) val;
|
||||
}
|
||||
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());
|
||||
} else if(val instanceof Double) {
|
||||
return (Double) val;
|
||||
} else if(val instanceof Integer) {
|
||||
return (Double) val;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
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());
|
||||
} else if(val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
} else 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());
|
||||
} else if(val instanceof Long) {
|
||||
return (Long) val;
|
||||
} else if(val instanceof Double) {
|
||||
return ((Double) val).longValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String getString(Map map, String key) {
|
||||
return (String) map.get(key);
|
||||
}
|
||||
|
||||
private boolean commitToDB(DiagnosticInfo diagnosticInfo) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
HeartBeat committed = dataManager.commit(diagnosticInfo);
|
||||
return PersistenceHelper.isManaged(committed);
|
||||
}
|
||||
|
||||
private Terminal loadTerminalBySN(String terminalSN) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where lower(t.sn)=:sn and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("sn", terminalSN.toLowerCase());
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,223 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.Application;
|
||||
import com.cmobile.unifiedtms.entity.DownloadTask;
|
||||
import com.cmobile.unifiedtms.entity.DownloadTaskLog;
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.enums.DownloadTaskActivity;
|
||||
import com.cmobile.unifiedtms.entity.enums.DownloadTimeType;
|
||||
import com.cmobile.unifiedtms.entity.enums.InstallationTimeType;
|
||||
import com.cmobile.unifiedtms.entity.enums.TaskStatus;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.Transaction;
|
||||
import com.haulmont.cuba.core.app.LockService;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.io.InputStream;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class DownloadTaskAckListener implements IncomingMessageListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(DownloadTaskAckListener.class);
|
||||
private Authentication authentication;
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
private Persistence persistence;
|
||||
|
||||
public DownloadTaskAckListener(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public void setPersistence(Persistence persistence) {
|
||||
this.persistence = persistence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message) {
|
||||
try {
|
||||
String json = message;
|
||||
Gson gson = new Gson();
|
||||
Map map = gson.fromJson(json, Map.class);
|
||||
logger.debug("Incoming MAP: " + map);
|
||||
|
||||
logger.debug("Before Authentication ...");
|
||||
authentication.begin();
|
||||
logger.debug("Authenticated");
|
||||
|
||||
String type = (String) map.get("req_type");
|
||||
if("ACK_DOWNLOAD_APP_TASK".equals(type)) {
|
||||
logger.debug("Detected Incoming: Ack of Download Task ...");
|
||||
|
||||
UUID reqId = UUID.fromString((String) map.get("req_id"));
|
||||
reqId = UUID.randomUUID();
|
||||
String terminalSN = (String) map.get("device_sn");
|
||||
Terminal terminal = loadTerminalBySN(terminalSN);
|
||||
UUID ackId = UUID.fromString((String) map.get("ack_id"));
|
||||
DownloadTaskLog log = loadDownloadTaskLog(ackId);
|
||||
|
||||
if(terminal != null) {
|
||||
if(log != null) {
|
||||
String status = (String) map.get("status");
|
||||
if("ACCEPTED".equals(status)) {
|
||||
status = "NOT_STARTED";
|
||||
}
|
||||
DownloadTaskActivity activity = DownloadTaskActivity.valueOf(status);
|
||||
log.setActivity(activity);
|
||||
|
||||
boolean saved = commitToDB(log);
|
||||
logger.debug("Save Ack to DB: " + saved);
|
||||
if(saved) {
|
||||
if("INSTALL_SUCCESS".equals(status) ||
|
||||
"INSTALL_FAIL".equals(status)) {
|
||||
// send ack DOWNLOAD_TASK_CONFIRM_ACK
|
||||
broadcastDownloadTasksConfirmACK(connector, terminalSN, ackId);
|
||||
}
|
||||
|
||||
int totalUnfinishedTask = getUnfinishedTask(log.getTask().getId());
|
||||
if(totalUnfinishedTask == 0) {
|
||||
// mark task as complete
|
||||
DownloadTask task = log.getTask();
|
||||
task.setStatus(TaskStatus.DONE);
|
||||
boolean taskCommitted = commitToDB(task);
|
||||
logger.debug("Save Task to DB: " + taskCommitted);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("Invalid Download Task Log (will give successful response): {}", ackId);
|
||||
// send ack DOWNLOAD_TASK_CONFIRM_ACK
|
||||
broadcastDownloadTasksConfirmACK(connector, terminalSN, ackId);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Invalid Terminal: {}", terminalSN);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// not my part
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error handling incoming message: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
authentication.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// send download task to device topic via mqtt
|
||||
public void broadcastDownloadTasksConfirmACK(MqttConnector mqttConnector,
|
||||
String terminalSN,
|
||||
UUID 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.toString());
|
||||
messageMap.put("req_type", "DOWNLOAD_TASK_CONFIRM_ACK");
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.debug("Try publish message to: {}", topicName);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
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 int getInt(Map map, String key) {
|
||||
Object val = map.get(key);
|
||||
if(val instanceof String) {
|
||||
return Integer.parseInt((String) val);
|
||||
} else if(val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
} else if(val instanceof Double) {
|
||||
return ((Double) val).intValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String getString(Map map, String key) {
|
||||
return (String) map.get(key);
|
||||
}
|
||||
|
||||
private boolean commitToDB(DownloadTaskLog log) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
DownloadTaskLog committed = dataManager.commit(log);
|
||||
//return PersistenceHelper.isManaged(committed);
|
||||
return committed != null;
|
||||
}
|
||||
|
||||
private boolean commitToDB(DownloadTask task) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
DownloadTask committed = dataManager.commit(task);
|
||||
//return PersistenceHelper.isManaged(committed);
|
||||
return committed != null;
|
||||
}
|
||||
|
||||
private Terminal loadTerminalBySN(String terminalSN) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where lower(t.sn)=:sn and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("sn", terminalSN.toLowerCase());
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
|
||||
private DownloadTaskLog loadDownloadTaskLog(UUID logId) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<DownloadTaskLog> loadContext = LoadContext.create(DownloadTaskLog.class);
|
||||
loadContext.setQueryString("select t from tms_DownloadTaskLog t where t.id=:id");
|
||||
loadContext.getQuery().setParameter("id", logId);
|
||||
loadContext.setView("downloadTaskLog-view");
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
|
||||
private Integer getUnfinishedTask(UUID downloadTaskId) {
|
||||
Double value = _getValueFromQuery("select count(*) from public.tms_download_task_log where task_id = '" + downloadTaskId.toString() + "' and activity not in (4,6,7);");
|
||||
logger.debug("--> value: '" + value + "'");
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Double _getValueFromQuery(String query) {
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
Set<Double> numbers = runner.query(query,
|
||||
new ResultSetHandler<Set<Double>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<Double> handle(ResultSet rs) throws SQLException {
|
||||
Set<Double> rows = new HashSet<Double>();
|
||||
while (rs.next()) {
|
||||
rows.add(rs.getDouble(1));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
});
|
||||
return numbers.isEmpty() ? null : numbers.toArray(new Double[] {})[0];
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.google.gson.Gson;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class EchoListener implements IncomingMessageListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(EchoListener.class);
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
public EchoListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message) {
|
||||
try {
|
||||
String json = message;
|
||||
Gson gson = new Gson();
|
||||
Map map = gson.fromJson(json, Map.class);
|
||||
logger.debug("Incoming MAP: " + map);
|
||||
|
||||
String type = (String) map.get("req_type");
|
||||
if("ECHO".equals(type)) {
|
||||
logger.debug("Detected Incoming: Echo ...");
|
||||
|
||||
UUID reqId = UUID.fromString((String) map.get("req_id"));
|
||||
String _reqTime = (String) map.get("req_time");
|
||||
reqId = UUID.randomUUID();
|
||||
Date reqTime = dateTimeFormat.parse(_reqTime);
|
||||
|
||||
connector.echoReceived(reqTime);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// not my part
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error handling incoming message: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.HeartBeat;
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.enums.TerminalHeartBeatStatus;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HeartBeatListener implements IncomingMessageListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(HeartBeatListener.class);
|
||||
private Authentication authentication;
|
||||
|
||||
public HeartBeatListener(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message) {
|
||||
try {
|
||||
String json = message;
|
||||
Gson gson = new Gson();
|
||||
Map map = gson.fromJson(json, Map.class);
|
||||
logger.debug("Incoming MAP: " + map);
|
||||
|
||||
logger.debug("Before Authentication ...");
|
||||
authentication.begin();
|
||||
logger.debug("Authenticated");
|
||||
|
||||
String type = (String) map.get("req_type");
|
||||
if("HEARTBEAT".equals(type)) {
|
||||
logger.debug("Detected Incoming: Heart Beat ...");
|
||||
|
||||
UUID reqId = UUID.fromString((String) map.get("req_id"));
|
||||
reqId = UUID.randomUUID();
|
||||
String terminalSN = (String) map.get("device_sn");
|
||||
|
||||
HeartBeat heartBeat = new HeartBeat();
|
||||
heartBeat.setId(reqId);
|
||||
heartBeat.setCreateTs(new Date());
|
||||
heartBeat.setCreatedBy(terminalSN);
|
||||
heartBeat.setVersion(1);
|
||||
Terminal terminal = loadTerminalBySN(terminalSN);
|
||||
if(terminal != null) {
|
||||
// diagnostic info - mandatory
|
||||
Map diagnosticInfoMap = (Map) map.get("diagnostic_info");
|
||||
|
||||
heartBeat.setTerminal(terminal);
|
||||
heartBeat.setBatteryPercentage(getInt(diagnosticInfoMap, "battery_percentage"));
|
||||
heartBeat.setBatteryTemp((Double) diagnosticInfoMap.get("battery_temp"));
|
||||
|
||||
// location info - mandatory
|
||||
Map locationInfoMap = (Map) map.get("location_info");
|
||||
heartBeat.setLatitude((Double) locationInfoMap.get("lat"));
|
||||
heartBeat.setLongitude((Double) locationInfoMap.get("lng"));
|
||||
boolean saved = commitToDB(heartBeat);
|
||||
logger.debug("Save Heart Beat to DB: " + saved);
|
||||
|
||||
// set as ONLINE
|
||||
terminal.setHeartBeatStatus(TerminalHeartBeatStatus.ONLINE);
|
||||
terminal.setLastHeartBeatTime(new Date());
|
||||
terminal.setLastHeartBeatId(heartBeat.getId());
|
||||
|
||||
boolean terminalSaved = commitToDB(terminal);
|
||||
logger.debug("Save Terminal's Heart Beat to DB: " + terminalSaved);
|
||||
} else {
|
||||
logger.debug("Invalid Terminal: {}", terminalSN);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// not my part
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error handling incoming message: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
authentication.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getInt(Map map, String key) {
|
||||
Object val = map.get(key);
|
||||
if(val instanceof String) {
|
||||
return Integer.parseInt((String) val);
|
||||
} else if(val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
} else if(val instanceof Double) {
|
||||
return ((Double) val).intValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean commitToDB(HeartBeat heartBeat) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
HeartBeat committed = dataManager.commit(heartBeat);
|
||||
return PersistenceHelper.isManaged(committed);
|
||||
}
|
||||
|
||||
private boolean commitToDB(Terminal terminal) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
Terminal committed = dataManager.commit(terminal);
|
||||
return PersistenceHelper.isManaged(committed);
|
||||
}
|
||||
|
||||
private Terminal loadTerminalBySN(String terminalSN) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where lower(t.sn)=:sn and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("sn", terminalSN.toLowerCase());
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
|
||||
public interface IncomingMessageListener {
|
||||
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message);
|
||||
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
package com.cmobile.unifiedtms.core.mqtt.listener;
|
||||
|
||||
import com.cmobile.unifiedtms.core.config.FileDownloadConfig;
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.*;
|
||||
import com.cmobile.unifiedtms.entity.enums.DownloadTaskActivity;
|
||||
import com.cmobile.unifiedtms.entity.enums.DownloadTimeType;
|
||||
import com.cmobile.unifiedtms.entity.enums.InstallationTimeType;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.Transaction;
|
||||
import com.haulmont.cuba.core.app.LockService;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PendingTaskPushListener<LoginService> implements IncomingMessageListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(PendingTaskPushListener.class);
|
||||
private Authentication authentication;
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
private FileDownloadConfig fileDownloadConfig;
|
||||
|
||||
public PendingTaskPushListener(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public void setFileDownloadConfig(FileDownloadConfig fileDownloadConfig) {
|
||||
this.fileDownloadConfig = fileDownloadConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingMessage(MqttConnector connector, String topic, String message) {
|
||||
try {
|
||||
logger.debug("Listener Start ..");
|
||||
String json = message;
|
||||
Gson gson = new Gson();
|
||||
Map map = gson.fromJson(json, Map.class);
|
||||
logger.debug("Incoming MAP: " + map);
|
||||
|
||||
logger.debug("Before Authentication ...");
|
||||
authentication.begin();
|
||||
logger.debug("Authenticated");
|
||||
|
||||
String type = (String) map.get("req_type");
|
||||
String terminalSN = (String) map.get("device_sn");
|
||||
if(terminalSN != null) {
|
||||
Terminal terminal = loadTerminalBySN(terminalSN);
|
||||
if (terminal != null) {
|
||||
List<DeleteTaskLog> deleteTaskLogs = getPendingDeleteTaskLogForTerminal(terminal);
|
||||
List<DownloadTaskLog> downloadTaskLogs = getPendingDownloadTaskLogForTerminal(terminal);
|
||||
|
||||
logger.debug("Total Pending Delete Tasks: {}", deleteTaskLogs.size());
|
||||
deleteTaskLogs.forEach(log -> {
|
||||
try {
|
||||
broadcastDeleteTasksToDevice(connector, log);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Error Broadcast Delete Tasks: {}", ex.getMessage(), ex);
|
||||
}
|
||||
});
|
||||
|
||||
logger.debug("Total Pending Download Logs: {}", downloadTaskLogs.size());
|
||||
downloadTaskLogs.forEach(log -> {
|
||||
try {
|
||||
broadcastDownloadTasksToDevice(connector, log);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Error Broadcast Download Tasks: {}", ex.getMessage(), ex);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logger.debug("Invalid Terminal: {}", terminalSN);
|
||||
}
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error handling incoming message: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
authentication.end();
|
||||
logger.debug("Listener End ..");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// send download task to device topic via mqtt
|
||||
public void broadcastDownloadTasksToDevice(MqttConnector mqttConnector,
|
||||
DownloadTaskLog log) throws Exception {
|
||||
DownloadTask task = log.getTask();
|
||||
Terminal terminal = log.getTerminal();
|
||||
Application app = log.getApplication();
|
||||
Persistence persistence = AppBeans.get(Persistence.NAME);
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LockService lockService = AppBeans.get(LockService.NAME);
|
||||
FileLoader fileLoader = AppBeans.get(FileLoader.NAME);
|
||||
logger.debug("Broadcast tasks ...");
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
try {
|
||||
lockService.lock(log);
|
||||
|
||||
int qos = 2;
|
||||
String topicName = terminal.getSn().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", log.getId().toString());
|
||||
messageMap.put("req_type", "DOWNLOAD_APP_TASK");
|
||||
// task map
|
||||
Map taskMap = new HashMap<>();
|
||||
taskMap.put("id", task.getId().toString());
|
||||
taskMap.put("name", task.getName());
|
||||
taskMap.put("download_time_type", task.getDownloadTimeType().name());
|
||||
if(task.getDownloadTimeType() == DownloadTimeType.DATETIME) {
|
||||
taskMap.put("download_time", dateTimeFormat.format(task.getDownloadTime()));
|
||||
}
|
||||
taskMap.put("installation_time_type", task.getInstallationTimeType().name());
|
||||
if(task.getInstallationTimeType() == InstallationTimeType.DATETIME) {
|
||||
taskMap.put("installation_time", dateTimeFormat.format(task.getInstallationTime()));
|
||||
}
|
||||
taskMap.put("installation_notification", task.getInstallationNotification().name());
|
||||
messageMap.put("task", taskMap);
|
||||
// app map
|
||||
Map appMap = new HashMap<>();
|
||||
appMap.put("id", app.getId().toString());
|
||||
appMap.put("name", app.getName());
|
||||
appMap.put("package_name", app.getPackageName());
|
||||
appMap.put("version", app.getAppVersion());
|
||||
appMap.put("company", app.getCompanyName());
|
||||
appMap.put("uninstallable", app.getUninstallable());
|
||||
appMap.put("description", app.getDescription());
|
||||
InputStream is = fileLoader.openStream(app.getApk());
|
||||
String downloadURL = fileDownloadConfig.getFileDownloadURL();
|
||||
if(downloadURL != null) {
|
||||
downloadURL = downloadURL.replace("{$id}", app.getApk().getId().toString());
|
||||
}
|
||||
appMap.put("download_url", downloadURL);
|
||||
|
||||
appMap.put("md5_checksum", md5Checksum(is));
|
||||
messageMap.put("app", appMap);
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.debug("Try publish message to: {}", topicName);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
logger.debug("Message published!");
|
||||
|
||||
// change log's last broadcast time
|
||||
log.setLastBroadcastTs(new Date());
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(log);
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error publish to device: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
lockService.unlock(log);
|
||||
}
|
||||
dataManager.commit(commitContext);
|
||||
tx.commit();
|
||||
} finally {
|
||||
logger.debug("Start broadcast download tasks DONE");
|
||||
}
|
||||
}
|
||||
|
||||
// send delete task to device topic via mqtt
|
||||
public void broadcastDeleteTasksToDevice(MqttConnector mqttConnector,
|
||||
DeleteTaskLog log) throws Exception {
|
||||
DeleteTask task = log.getTask();
|
||||
Terminal terminal = log.getTerminal();
|
||||
ApplicationSimple app = log.getApplication();
|
||||
Persistence persistence = AppBeans.get(Persistence.NAME);
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LockService lockService = AppBeans.get(LockService.NAME);
|
||||
FileLoader fileLoader = AppBeans.get(FileLoader.NAME);
|
||||
logger.debug("Broadcast tasks ...");
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
try {
|
||||
lockService.lock(log);
|
||||
|
||||
int qos = 2;
|
||||
String topicName = terminal.getSn().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", log.getId().toString());
|
||||
messageMap.put("req_type", "DELETE_APP_TASK");
|
||||
// task map
|
||||
Map taskMap = new HashMap<>();
|
||||
taskMap.put("id", task.getId().toString());
|
||||
taskMap.put("name", task.getName());
|
||||
taskMap.put("delete_time", task.getDeleteTime() != null ? dateTimeFormat.format(task.getDeleteTime()) : null);
|
||||
messageMap.put("task", taskMap);
|
||||
// app map
|
||||
Map appMap = new HashMap<>();
|
||||
appMap.put("id", app.getId().toString());
|
||||
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);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
logger.debug("Message published!");
|
||||
|
||||
// change log's last broadcast time
|
||||
log.setLastBroadcastTs(new Date());
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(log);
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error publish to device: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
lockService.unlock(log);
|
||||
}
|
||||
dataManager.commit(commitContext);
|
||||
tx.commit();
|
||||
} finally {
|
||||
logger.debug("Start broadcast delete tasks DONE");
|
||||
}
|
||||
}
|
||||
|
||||
private String md5Checksum(InputStream is) throws Exception {
|
||||
try {
|
||||
String checksum = DigestUtils.md5Hex(is);
|
||||
return checksum;
|
||||
} finally {
|
||||
if(is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getInt(Map map, String key) {
|
||||
Object val = map.get(key);
|
||||
if(val instanceof String) {
|
||||
return Integer.parseInt((String) val);
|
||||
} else if(val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
} else if(val instanceof Double) {
|
||||
return ((Double) val).intValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean commitToDB(HeartBeat heartBeat) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
HeartBeat committed = dataManager.commit(heartBeat);
|
||||
return PersistenceHelper.isManaged(committed);
|
||||
}
|
||||
|
||||
private Terminal loadTerminalBySN(String terminalSN) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where lower(t.sn)=:sn and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("sn", terminalSN.toLowerCase());
|
||||
return dataManager.load(loadContext);
|
||||
}
|
||||
|
||||
private List<DownloadTaskLog> getPendingDownloadTaskLogForTerminal(Terminal terminal) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<DownloadTaskLog> loadContext = LoadContext.create(DownloadTaskLog.class);
|
||||
loadContext.setQueryString("select t from tms_DownloadTaskLog t where t.terminal.id=:terminalId and t.activity=:activity order by t.createTs desc");
|
||||
loadContext.getQuery().setParameter("terminalId", terminal.getId());
|
||||
loadContext.getQuery().setParameter("activity", DownloadTaskActivity.INITIAL);
|
||||
loadContext.setView("downloadTaskLog-with-task-view");
|
||||
return dataManager.loadList(loadContext);
|
||||
}
|
||||
|
||||
private List<DeleteTaskLog> getPendingDeleteTaskLogForTerminal(Terminal terminal) {
|
||||
DataManager dataManager = AppBeans.get(DataManager.NAME);
|
||||
LoadContext<DeleteTaskLog> loadContext = LoadContext.create(DeleteTaskLog.class);
|
||||
loadContext.setQueryString("select t from tms_DeleteTaskLog t where t.terminal.id=:terminalId and t.activity=:activity order by t.createTs desc");
|
||||
loadContext.getQuery().setParameter("terminalId", terminal.getId());
|
||||
loadContext.getQuery().setParameter("activity", DownloadTaskActivity.INITIAL);
|
||||
loadContext.setView("deleteTaskLog-with-task-view");
|
||||
return dataManager.loadList(loadContext);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package com.cmobile.unifiedtms.core.security;
|
||||
|
||||
import com.haulmont.cuba.core.global.Messages;
|
||||
import com.haulmont.cuba.security.app.UserSessionsAPI;
|
||||
import com.haulmont.cuba.security.auth.AbstractClientCredentials;
|
||||
import com.haulmont.cuba.security.auth.events.BeforeLoginEvent;
|
||||
import com.haulmont.cuba.security.global.LoginException;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
public class MyLoginEventListener {
|
||||
@Inject
|
||||
private UserSessionsAPI userSessions;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
|
||||
@EventListener
|
||||
private void onBeforeLogin(BeforeLoginEvent event) throws LoginException {
|
||||
if (event.getCredentials() instanceof AbstractClientCredentials) {
|
||||
String login = ((AbstractClientCredentials) event.getCredentials()).getUserIdentifier();
|
||||
if(login.contains("rest")) return;
|
||||
Locale locale = ((AbstractClientCredentials) event.getCredentials()).getLocale();
|
||||
/*if (checkConcurrentUsers(login))
|
||||
throw new LoginException(messages.getMessage(getClass(), "LoginException.concurrentUsersLimitExceeded", locale));
|
||||
*/
|
||||
abortOtherLogins(login);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if user limit is not exceeded
|
||||
*/
|
||||
protected boolean checkConcurrentUsers(String login) {
|
||||
/*logger.info("Logged in users");
|
||||
userSessions.getUserSessionsStream()
|
||||
.forEach(userSession -> {
|
||||
logger.info("Logged in: " + userSession.getUser().getLogin());
|
||||
});
|
||||
logger.info("End of Logged in users");
|
||||
*/
|
||||
return userSessions.getUserSessionsStream()
|
||||
.anyMatch(s -> !s.isSystem() && Objects.equals(s.getUser().getLogin(), login));
|
||||
}
|
||||
|
||||
protected void abortOtherLogins(String login) {
|
||||
userSessions.getUserSessionsStream()
|
||||
.forEach(userSession -> {
|
||||
String userLogin = userSession.getUser().getLogin();
|
||||
if(login.equalsIgnoreCase(userLogin)) {
|
||||
userSessions.killSession(userSession.getId());
|
||||
logger.info("Kill session id: " + userSession.getId() + " for user: " + userSession.getUser().getLogin());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package com.cmobile.unifiedtms.core.security;
|
||||
|
||||
import com.cmobile.unifiedtms.security.UserSessionExistsException;
|
||||
import com.haulmont.cuba.security.app.UserSessionsAPI;
|
||||
import com.haulmont.cuba.security.auth.AbstractClientCredentials;
|
||||
import com.haulmont.cuba.security.auth.AuthenticationDetails;
|
||||
import com.haulmont.cuba.security.auth.Credentials;
|
||||
import com.haulmont.cuba.security.auth.UserAccessChecker;
|
||||
import com.haulmont.cuba.security.global.LoginException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component(UserSessionExistsAccessChecker.NAME)
|
||||
public class UserSessionExistsAccessChecker implements UserAccessChecker, Ordered {
|
||||
public static final String NAME = "scm_UserSessionExistsAccessChecker";
|
||||
|
||||
@Inject
|
||||
protected UserSessionsAPI userSessions;
|
||||
|
||||
@Override
|
||||
public void check(Credentials credentials, AuthenticationDetails authenticationDetails) throws LoginException {
|
||||
if (credentials instanceof AbstractClientCredentials) {
|
||||
AbstractClientCredentials clientCredentials = (AbstractClientCredentials) credentials;
|
||||
if(clientCredentials.getUserIdentifier().contains("rest")) return;
|
||||
if (clientCredentials.getParams() != null && Boolean.TRUE.equals(clientCredentials.getParams().get("abortSession"))) {
|
||||
userSessions.getUserSessionsStream()
|
||||
.filter(s -> !s.isSystem() && Objects.equals(s.getUser().getLogin(), clientCredentials.getUserIdentifier()))
|
||||
.findAny()
|
||||
.ifPresent(s -> userSessions.killSession(s.getId()));
|
||||
}
|
||||
if (checkExistsUser(clientCredentials.getUserIdentifier()))
|
||||
throw new UserSessionExistsException(clientCredentials.getUserIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean checkExistsUser(String login) {
|
||||
return userSessions.getUserSessionsStream()
|
||||
.anyMatch(s -> !s.isSystem() && Objects.equals(s.getUser().getLogin(), login));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return HIGHEST_PLATFORM_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
LoginException.concurrentUsersLimitExceeded = User already logged in!!
|
||||
@ -0,0 +1 @@
|
||||
LoginException.concurrentUsersLimitExceeded = Pengguna sudah login!!
|
||||
35
modules/core/src/com/cmobile/unifiedtms/fts.xml
Normal file
35
modules/core/src/com/cmobile/unifiedtms/fts.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<fts-config xmlns="http://schemas.haulmont.com/cuba/fts.xsd">
|
||||
<entities>
|
||||
<entity class="com.cmobile.unifiedtms.entity.HeartBeat">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.DiagnosticInfo">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.DownloadTask">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.DownloadTaskLog">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.TerminalLastHeartBeat">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.TerminalLastDiagnosticInfo">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.ApplicationSimple">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.DeleteTaskLog">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.TerminalLink">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
<entity class="com.cmobile.unifiedtms.entity.TerminalReport">
|
||||
<include re=".*"/>
|
||||
</entity>
|
||||
</entities>
|
||||
</fts-config>
|
||||
@ -0,0 +1,69 @@
|
||||
package com.cmobile.unifiedtms.listeners;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.Events;
|
||||
import com.haulmont.cuba.core.sys.events.AppContextInitializedEvent;
|
||||
import com.haulmont.cuba.core.sys.events.AppContextStartedEvent;
|
||||
import com.haulmont.cuba.core.sys.events.AppContextStoppedEvent;
|
||||
import com.haulmont.cuba.core.sys.servlet.events.ServletContextDestroyedEvent;
|
||||
import com.haulmont.cuba.core.sys.servlet.events.ServletContextInitializedEvent;
|
||||
import java.io.File;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.license4j.util.FileUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Component("tms_AppLifecycleEventListener_ApplicationContextListener")
|
||||
public class AppLifecycleEventListener {
|
||||
|
||||
@Inject
|
||||
private Logger log;
|
||||
|
||||
@EventListener
|
||||
@Order(Events.LOWEST_PLATFORM_PRECEDENCE + 100)
|
||||
public void applicationContextInitialized(AppContextInitializedEvent event) {
|
||||
log.info("Application context is Initialized");
|
||||
MqttConnector mqttConnector = AppBeans.get(MqttConnector.NAME);
|
||||
|
||||
// validate license
|
||||
validateLicense();
|
||||
}
|
||||
|
||||
private void validateLicense() throws RuntimeException {
|
||||
/*
|
||||
String appHome = System.getProperty("app.home");
|
||||
String licenseFile = appHome + File.separator + "license.l4j";
|
||||
try {
|
||||
String licenseString = FileUtils.readFile("D:\\Documents\\Personal\\Verifone\\UTMS\\Licenses\\1626785999496.l4j");
|
||||
String publickey = "30820122300d06092a864886f70d01010105000382010f00303032301006072a8648ce3d02002EC311215SHA512withECDSA106052b81040006031e0004125ca10acdc54807553b11bb9b4f59fe947f68788fe9e6f0d324a03bG82010a0282010100859c388d100cacf014e7f61be9d30f5c262feadf8440affe93a85021e212a808ff6aa40e2d318ef6b298f61a68fc8bde2237fbd9241988fb7a6fe07138bab8ace340e907213daaeb7121747c5f022bd7ee5f49d49dba64883fa0674403RSA4204813SHA512withRSA7672242c571f7edb5966469150d9240464c64087727561c8cc40609b8ff0ea7e07bd08d643f6c04ef3c860092354be465bb3136cb517b0cbb5f5718f8fd95e95a33b848f01cec185afbb43062af5c8ff7f2030783371b6dbbd4f7230b1725b4dc846d1a40d7b38d369ee6df56d8a8648858b5f5b6aac61c12698dfac749bd112443bec8b9bc13e9c405053200789664afee8ed6b7dd52b264f1f58e6843bd18ae5eab76b0203010001";
|
||||
String productID = "utms";
|
||||
String productEdition = "Production";
|
||||
String productVersion = "1.0";
|
||||
} finally {}
|
||||
*/
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void applicationContextStarted(AppContextStartedEvent event) {
|
||||
log.info("Application context is Started");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void applicationContextStopped(AppContextStoppedEvent event) {
|
||||
log.info("Application context is Stopped");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void servletContextInitializedEvent(ServletContextInitializedEvent event) {
|
||||
log.info("Application and servlet context is initialized");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void servletContextDestroyedEvent(ServletContextDestroyedEvent event) {
|
||||
log.info("Application is about to shut down, all contexts are now destroyed");
|
||||
}
|
||||
}
|
||||
2821
modules/core/src/com/cmobile/unifiedtms/service/APIServiceBean.java
Normal file
2821
modules/core/src/com/cmobile/unifiedtms/service/APIServiceBean.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.pras.Extract;
|
||||
import com.pras.Manifest;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Service(ApkService.NAME)
|
||||
public class ApkServiceBean implements ApkService {
|
||||
|
||||
@Override
|
||||
public Manifest extractManifest(byte[] bytes) throws Exception {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new ByteArrayInputStream(bytes);
|
||||
Extract extract = new Extract();
|
||||
Manifest manifest = extract.extractManifest(in);
|
||||
return manifest;
|
||||
} finally {
|
||||
if(in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch(IOException ignore) {}
|
||||
in = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.chartdata.PieChartData;
|
||||
import com.haulmont.addon.sdbmt.core.app.multitenancy.TenantProvider;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.*;
|
||||
|
||||
@Service(ChartDataService.NAME)
|
||||
public class ChartDataServiceBean implements ChartDataService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(ChartDataServiceBean.class);
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
@Inject
|
||||
private TenantProvider tenantProvider;
|
||||
|
||||
@Override
|
||||
public List<PieChartData> getDownloadStats() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
String query;
|
||||
String[] params = new String[] {};
|
||||
int[] types = new int[] {};
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
query = ""
|
||||
+ "select task.id, task.name, "
|
||||
+ " sum(case when log.activity=1 then 1 else 0 end) total_not_downloaded, "
|
||||
+ " sum(case when log.activity=3 then 1 else 0 end) total_downloaded, "
|
||||
+ " sum(case when log.activity=5 then 1 else 0 end) total_tobe_installed, "
|
||||
+ " sum(case when log.activity=6 then 1 else 0 end) total_installed "
|
||||
+ "from tms_download_task task "
|
||||
+ " left join tms_download_task_log log on log.task_id=task.id "
|
||||
+ "where task.delete_ts is null "
|
||||
+ "group by task.id,task.name,task.create_ts "
|
||||
+ "order by task.create_ts desc "
|
||||
+ "limit 2";
|
||||
} else {
|
||||
params = new String[] { tenantId };
|
||||
types = new int[] { Types.VARCHAR };
|
||||
query = ""
|
||||
+ "select task.id, task.name, "
|
||||
+ " sum(case when log.activity=1 then 1 else 0 end) total_not_downloaded, "
|
||||
+ " sum(case when log.activity=3 then 1 else 0 end) total_downloaded, "
|
||||
+ " sum(case when log.activity=5 then 1 else 0 end) total_tobe_installed, "
|
||||
+ " sum(case when log.activity=6 then 1 else 0 end) total_installed "
|
||||
+ "from tms_download_task task "
|
||||
+ " left join tms_download_task_log log on log.task_id=task.id "
|
||||
+ "where task.tenant_id=? and task.delete_ts is null "
|
||||
+ "group by task.id,task.name,task.create_ts "
|
||||
+ "order by task.create_ts desc "
|
||||
+ "limit 2";
|
||||
}
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
Set<PieChartData> datas = runner.query(query,
|
||||
params,
|
||||
types,
|
||||
new ResultSetHandler<Set<PieChartData>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<PieChartData> handle(ResultSet rs) throws SQLException {
|
||||
Set<PieChartData> rows = new HashSet<PieChartData>();
|
||||
while (rs.next()) {
|
||||
String id = rs.getString(1);
|
||||
String name = rs.getString(2);
|
||||
int notDownloaded = rs.getInt(3);
|
||||
int downloaded = rs.getInt(4);
|
||||
int tobeInstalled = rs.getInt(5);
|
||||
int installed = rs.getInt(6);
|
||||
|
||||
PieChartData data = new PieChartData(id, name);
|
||||
data.addValue("Not Downloaded", notDownloaded);
|
||||
data.addValue("Downloaded", downloaded);
|
||||
data.addValue("To be Installed", tobeInstalled);
|
||||
data.addValue("Installed", installed);
|
||||
rows.add(data);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
});
|
||||
return new ArrayList<>(datas);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,226 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.*;
|
||||
import com.cmobile.unifiedtms.entity.enums.*;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.Transaction;
|
||||
import com.haulmont.cuba.core.app.LockService;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service(DeleteTaskService.NAME)
|
||||
public class DeleteTaskServiceBean implements DeleteTaskService {
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
@Inject
|
||||
private LockService lockService;
|
||||
@Inject
|
||||
private Metadata metadata;
|
||||
@Inject
|
||||
private MqttConnector mqttConnector;
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
@Override
|
||||
public void startDeleteTasks() throws Exception {
|
||||
logger.info("Start delete tasks ...");
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// load all NOT_STARTED download tasks
|
||||
LoadContext<DeleteTask> loadContext = LoadContext.create(DeleteTask.class);
|
||||
loadContext.setQueryString("select t from tms_DeleteTask t where t.deleteTs is null and t.status=:status");
|
||||
loadContext.getQuery().setParameter("status", TaskStatus.NOT_STARTED);
|
||||
loadContext.setView("deleteTask-view");
|
||||
List<DeleteTask> tasks = dataManager.loadList(loadContext);
|
||||
|
||||
logger.info("Total tasks: {}", tasks.size());
|
||||
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
tasks.forEach(task -> {
|
||||
try {
|
||||
lockService.lock(task);
|
||||
|
||||
// get the apps
|
||||
List<ApplicationSimple> applications = task.getApplications();
|
||||
// get the terminals
|
||||
List<TerminalGroup> terminalGroups = task.getTerminalGroups();
|
||||
List<Terminal> terminals = new ArrayList<>();
|
||||
terminalGroups.forEach(terminalGroup -> {
|
||||
List<Terminal> _terminals = terminalGroup.getTerminals();
|
||||
_terminals.forEach(terminal -> {
|
||||
if(!terminals.contains(terminal)) {
|
||||
terminals.add(terminal);
|
||||
}
|
||||
});
|
||||
});
|
||||
// create the delete log for each app - terminal
|
||||
terminals.forEach(terminal -> {
|
||||
applications.forEach(app -> {
|
||||
DeleteTaskLog log = metadata.create(DeleteTaskLog.class);
|
||||
log.setApplication(app);
|
||||
log.setTerminal(terminal);
|
||||
log.setTask(task);
|
||||
log.setActivity(DeleteTaskActivity.INITIAL);
|
||||
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(log);
|
||||
});
|
||||
});
|
||||
// change task's status to IN_PROGRESS
|
||||
task.setStatus(TaskStatus.IN_PROGRESS);
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(task);
|
||||
} finally {
|
||||
lockService.unlock(task);
|
||||
}
|
||||
});
|
||||
dataManager.commit(commitContext);
|
||||
tx.commit();
|
||||
} finally {
|
||||
logger.info("Start delete tasks DONE");
|
||||
}
|
||||
}
|
||||
|
||||
// send to device topic via mqtt
|
||||
public void broadcastTasksToDevices() throws Exception {
|
||||
logger.info("Broadcast tasks ...");
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// load all INITIAL logs
|
||||
LoadContext<DeleteTaskLog> loadContext = LoadContext.create(DeleteTaskLog.class);
|
||||
loadContext.setQueryString("select t from tms_DeleteTaskLog t where t.activity=:activity");
|
||||
loadContext.getQuery().setParameter("activity", DownloadTaskActivity.INITIAL);
|
||||
loadContext.setView("deleteTaskLog-view");
|
||||
List<DeleteTaskLog> logs = dataManager.loadList(loadContext);
|
||||
|
||||
logger.info("Total logs: {}", logs.size());
|
||||
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
logs.forEach(log -> {
|
||||
try {
|
||||
// check last
|
||||
Date lastBroadcastTime = log.getLastBroadcastTs();
|
||||
if(lastBroadcastTime != null) {
|
||||
// check if less then 15 minutes
|
||||
long TEN_MINUTES = TimeUnit.MINUTES.toMillis(10);
|
||||
long elapsed = System.currentTimeMillis() - lastBroadcastTime.getTime();
|
||||
if(elapsed < TEN_MINUTES) {
|
||||
logger.info("Skipping coz < 10 minutes for SN: {}", log.getTerminal().getSn());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lockService.lock(log);
|
||||
|
||||
// terminal
|
||||
Terminal terminal = log.getTerminal();
|
||||
ApplicationSimple app = log.getApplication();
|
||||
DeleteTask task = log.getTask();
|
||||
|
||||
int qos = 2;
|
||||
String topicName = terminal.getSn().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", log.getId().toString());
|
||||
messageMap.put("req_type", "DELETE_APP_TASK");
|
||||
// task map
|
||||
Map taskMap = new HashMap<>();
|
||||
taskMap.put("id", task.getId().toString());
|
||||
taskMap.put("name", task.getName());
|
||||
taskMap.put("delete_time", task.getDeleteTime() != null ? dateTimeFormat.format(task.getDeleteTime()) : null);
|
||||
messageMap.put("task", taskMap);
|
||||
// app map
|
||||
Map appMap = new HashMap<>();
|
||||
appMap.put("id", app.getId().toString());
|
||||
appMap.put("name", app.getAppName());
|
||||
appMap.put("package_name", app.getPackageName());
|
||||
messageMap.put("app", appMap);
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.info("Try publish message to: {}", topicName);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
logger.info("Message published!");
|
||||
|
||||
// change log's last broadcast time
|
||||
log.setLastBroadcastTs(new Date());
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(log);
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error publish to device: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
lockService.unlock(log);
|
||||
}
|
||||
});
|
||||
dataManager.commit(commitContext);
|
||||
tx.commit();
|
||||
} finally {
|
||||
logger.info("Start broadcast delete tasks DONE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String updateTaskStatuses() throws Exception {
|
||||
String query = "select datas.arr[0] status,datas.arr[1] message from ("
|
||||
+ "select update_delete_task_status() arr "
|
||||
+ ") datas";
|
||||
String[] params = new String[] {
|
||||
};
|
||||
logger.info("update_delete_task_status()");
|
||||
int[] types = new int[] {
|
||||
};
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
String[] datas = runner.query(query,
|
||||
params,
|
||||
types,
|
||||
new ResultSetHandler<String[]>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String[] handle(ResultSet rs) throws SQLException {
|
||||
if(rs.next()) {
|
||||
String[] ret = new String[2];
|
||||
ret[0] = rs.getString(1);
|
||||
ret[1] = rs.getString(2);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(datas != null) {
|
||||
sb.append("[").append(datas[0]).append(", ").append(datas[1]).append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.MqttConfig;
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.DeviceProfile;
|
||||
import com.cmobile.unifiedtms.entity.DeviceProfileApp;
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.TerminalGroup;
|
||||
import com.google.gson.Gson;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
@Service(DeviceInitService.NAME)
|
||||
public class DeviceInitServiceBean implements DeviceInitService {
|
||||
|
||||
@Inject
|
||||
private MqttConfig mqttConfig;
|
||||
@Inject
|
||||
private MqttConnector connector;
|
||||
private Logger logger = LoggerFactory.getLogger(DeviceInitServiceBean.class);
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
|
||||
|
||||
@Override
|
||||
public void publishInit(DeviceProfile profile, Terminal terminal) {
|
||||
try {
|
||||
int qos = 2;
|
||||
String topicName = terminal.getSn().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_PROFILE");
|
||||
// profile map
|
||||
Map profileMap = new HashMap<>();
|
||||
profileMap.put("id", profile.getId().toString());
|
||||
profileMap.put("name", profile.getName());
|
||||
profileMap.put("hearbeat_interval", profile.getHeartbeatInterval());
|
||||
profileMap.put("diagnostic_interval", profile.getDiagnosticInterval());
|
||||
profileMap.put("mask_home_button", profile.getMaskHomeButton());
|
||||
profileMap.put("mask_status_button", profile.getMaskStatusBar());
|
||||
profileMap.put("schedule_reboot", profile.getScheduleReboot());
|
||||
if(profile.getScheduleReboot()) {
|
||||
profileMap.put("schedule_reboot_time", timeFormat.format(profile.getScheduleRebootTime()));
|
||||
}
|
||||
profileMap.put("relocation_alert", profile.getRelocationAlert());
|
||||
profileMap.put("moving_threshold", profile.getMovingThreshold());
|
||||
profileMap.put("admin_password", profile.getAdminPassword());
|
||||
|
||||
// mqtt host
|
||||
profileMap.put("host", mqttConfig.getDeviceHost());
|
||||
|
||||
// additional
|
||||
profileMap.put("front_app", profile.getFrontApp());
|
||||
List<DeviceProfileApp> apps = profile.getApps();
|
||||
List<String> appList = new ArrayList<>();
|
||||
for(DeviceProfileApp app : apps) {
|
||||
appList.add(app.getPackageName());
|
||||
}
|
||||
profileMap.put("apps_home_list", appList);
|
||||
messageMap.put("profile", profileMap);
|
||||
|
||||
Gson gson = new Gson();
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.info("Try publish message to: {}", topicName);
|
||||
connector.publishRetainedMessage(topicName, messageData, qos);
|
||||
//connector.publishMessage(topicName, messageData, qos);
|
||||
logger.info("Message published!");
|
||||
} catch(Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishInitToProfile(DeviceProfile profile) {
|
||||
try {
|
||||
int qos = 2;
|
||||
String topicName = profile.getId().toString();
|
||||
// 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_PROFILE");
|
||||
// profile map
|
||||
Map profileMap = new HashMap<>();
|
||||
profileMap.put("id", profile.getId().toString());
|
||||
profileMap.put("name", profile.getName());
|
||||
profileMap.put("hearbeat_interval", profile.getHeartbeatInterval());
|
||||
profileMap.put("diagnostic_interval", profile.getDiagnosticInterval());
|
||||
profileMap.put("mask_home_button", profile.getMaskHomeButton());
|
||||
profileMap.put("mask_status_button", profile.getMaskStatusBar());
|
||||
profileMap.put("schedule_reboot", profile.getScheduleReboot());
|
||||
if(profile.getScheduleReboot()) {
|
||||
profileMap.put("schedule_reboot_time", timeFormat.format(profile.getScheduleRebootTime()));
|
||||
}
|
||||
profileMap.put("relocation_alert", profile.getRelocationAlert());
|
||||
profileMap.put("moving_threshold", profile.getMovingThreshold());
|
||||
profileMap.put("admin_password", profile.getAdminPassword());
|
||||
|
||||
// mqtt host
|
||||
profileMap.put("host", mqttConfig.getDeviceHost());
|
||||
|
||||
// additional
|
||||
profileMap.put("front_app", profile.getFrontApp());
|
||||
List<DeviceProfileApp> apps = profile.getApps();
|
||||
List<String> appList = new ArrayList<>();
|
||||
for(DeviceProfileApp app : apps) {
|
||||
appList.add(app.getPackageName());
|
||||
}
|
||||
profileMap.put("apps_home_list", appList);
|
||||
messageMap.put("profile", profileMap);
|
||||
|
||||
Gson gson = new Gson();
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.info("Try publish message to: {}", topicName);
|
||||
connector.publishRetainedMessage(topicName, messageData, qos);
|
||||
//connector.publishMessage(topicName, messageData, qos);
|
||||
logger.info("Message published!");
|
||||
} catch(Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,335 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.config.BroadcastConfig;
|
||||
import com.cmobile.unifiedtms.core.config.ExtendedConfig;
|
||||
import com.cmobile.unifiedtms.core.config.FileDownloadConfig;
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.cmobile.unifiedtms.entity.*;
|
||||
import com.cmobile.unifiedtms.entity.enums.*;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.Transaction;
|
||||
import com.haulmont.cuba.core.app.LockService;
|
||||
import com.haulmont.cuba.core.entity.FileDescriptor;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service(DownloadTaskService.NAME)
|
||||
public class DownloadTaskServiceBean implements DownloadTaskService {
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
@Inject
|
||||
private LockService lockService;
|
||||
@Inject
|
||||
private Metadata metadata;
|
||||
@Inject
|
||||
private MqttConnector mqttConnector;
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
@Inject
|
||||
private FileLoader fileLoader;
|
||||
@Inject
|
||||
private FileDownloadConfig fileDownloadConfig;
|
||||
@Inject
|
||||
private BroadcastConfig broadcastConfig;
|
||||
@Inject
|
||||
private ExtendedConfig extendedConfig;
|
||||
|
||||
@Override
|
||||
public void startDownloadTasks() throws Exception {
|
||||
logger.info("Start download tasks ...");
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// load all NOT_STARTED download tasks
|
||||
LoadContext<DownloadTask> loadContext = LoadContext.create(DownloadTask.class);
|
||||
loadContext.setQueryString("select t from tms_DownloadTask t where t.deleteTs is null and t.status=:status");
|
||||
loadContext.getQuery().setParameter("status", TaskStatus.NOT_STARTED);
|
||||
loadContext.setView("downloadTask-view");
|
||||
List<DownloadTask> tasks = dataManager.loadList(loadContext);
|
||||
|
||||
logger.info("Total tasks: {}", tasks.size());
|
||||
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
tasks.forEach(task -> {
|
||||
try {
|
||||
lockService.lock(task);
|
||||
|
||||
boolean containBlackListed = false;
|
||||
String whitelistPackageNames = extendedConfig.getWhitelistPackageNames();
|
||||
int blacklistThreshold = extendedConfig.getBlacklistThreshold();
|
||||
logger.info("Whitelist package names: {}", whitelistPackageNames);
|
||||
logger.info("Blacklist threshold: {}", blacklistThreshold);
|
||||
|
||||
// get the apps
|
||||
List<Application> applications = task.getApplications();
|
||||
for(Application app : applications) {
|
||||
boolean whiteListed = app.getPackageName().matches(whitelistPackageNames);
|
||||
logger.info("Checking '{}' : {}", app.getPackageName(), whiteListed);
|
||||
containBlackListed |= !whiteListed;
|
||||
}
|
||||
|
||||
// get the terminals
|
||||
List<TerminalGroup> terminalGroups = task.getTerminalGroups();
|
||||
List<Terminal> terminals = new ArrayList<>();
|
||||
terminalGroups.forEach(terminalGroup -> {
|
||||
List<Terminal> _terminals = terminalGroup.getTerminals();
|
||||
_terminals.forEach(terminal -> {
|
||||
if(!terminals.contains(terminal)) {
|
||||
terminals.add(terminal);
|
||||
}
|
||||
});
|
||||
});
|
||||
int totalTerminal = terminals.size();
|
||||
boolean continueStart = !containBlackListed || (totalTerminal <= blacklistThreshold);
|
||||
logger.info("Contain blacklisted : {}, Total Terminal: {} => Continue start: {}", containBlackListed, totalTerminal, continueStart);
|
||||
|
||||
if(continueStart) {
|
||||
// create the download log for each app - terminal
|
||||
terminals.forEach(terminal -> {
|
||||
applications.forEach(app -> {
|
||||
DownloadTaskLog log = metadata.create(DownloadTaskLog.class);
|
||||
log.setApplication(app);
|
||||
log.setTerminal(terminal);
|
||||
log.setTask(task);
|
||||
log.setActivity(DownloadTaskActivity.INITIAL);
|
||||
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(log);
|
||||
});
|
||||
});
|
||||
// change task's status to IN_PROGRESS
|
||||
task.setStatus(TaskStatus.IN_PROGRESS);
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(task);
|
||||
} else {
|
||||
logger.info("Task contain package not in whitelist and total terminal > {}", blacklistThreshold);
|
||||
// cancel task directly
|
||||
// change task's status to CANCELED
|
||||
task.setStatus(TaskStatus.CANCELLED);
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(task);
|
||||
}
|
||||
} finally {
|
||||
lockService.unlock(task);
|
||||
}
|
||||
});
|
||||
dataManager.commit(commitContext);
|
||||
tx.commit();
|
||||
} finally {
|
||||
logger.info("Start download tasks DONE");
|
||||
}
|
||||
}
|
||||
|
||||
// send to device topic via mqtt
|
||||
public void broadcastTasksToDevices() throws Exception {
|
||||
logger.info("Broadcast tasks ...");
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// load all INITIAL logs
|
||||
LoadContext<DownloadTaskLog> loadContext = LoadContext.create(DownloadTaskLog.class);
|
||||
loadContext.setQueryString("select t from tms_DownloadTaskLog t where t.activity=:activity");
|
||||
loadContext.getQuery().setParameter("activity", DownloadTaskActivity.INITIAL);
|
||||
loadContext.setView("downloadTaskLog-view");
|
||||
List<DownloadTaskLog> logs = dataManager.loadList(loadContext);
|
||||
|
||||
logger.info("Total logs: {}", logs.size());
|
||||
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
logs.forEach(log -> {
|
||||
try {
|
||||
// check last
|
||||
Date lastBroadcastTime = log.getLastBroadcastTs();
|
||||
if(lastBroadcastTime != null) {
|
||||
// check if less then 15 minutes
|
||||
long TEN_MINUTES = TimeUnit.MINUTES.toMillis(10);
|
||||
long elapsed = System.currentTimeMillis() - lastBroadcastTime.getTime();
|
||||
if(elapsed < TEN_MINUTES) {
|
||||
logger.info("Skipping coz < 10 minutes for SN: {}", log.getTerminal().getSn());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lockService.lock(log);
|
||||
|
||||
// terminal
|
||||
Terminal terminal = log.getTerminal();
|
||||
Application app = log.getApplication();
|
||||
DownloadTask task = log.getTask();
|
||||
|
||||
int qos = 2;
|
||||
String topicName = terminal.getSn().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", log.getId().toString());
|
||||
messageMap.put("req_type", "DOWNLOAD_APP_TASK");
|
||||
// task map
|
||||
Map taskMap = new HashMap<>();
|
||||
taskMap.put("id", task.getId().toString());
|
||||
taskMap.put("name", task.getName());
|
||||
taskMap.put("download_time_type", task.getDownloadTimeType().name());
|
||||
if(task.getDownloadTimeType() == DownloadTimeType.DATETIME) {
|
||||
taskMap.put("download_time", dateTimeFormat.format(task.getDownloadTime()));
|
||||
}
|
||||
taskMap.put("installation_time_type", task.getInstallationTimeType().name());
|
||||
if(task.getInstallationTimeType() == InstallationTimeType.DATETIME) {
|
||||
taskMap.put("installation_time", dateTimeFormat.format(task.getInstallationTime()));
|
||||
}
|
||||
taskMap.put("installation_notification", task.getInstallationNotification().name());
|
||||
messageMap.put("task", taskMap);
|
||||
// app map
|
||||
Map appMap = new HashMap<>();
|
||||
appMap.put("id", app.getId().toString());
|
||||
appMap.put("name", app.getName());
|
||||
appMap.put("package_name", app.getPackageName());
|
||||
appMap.put("version", app.getAppVersion());
|
||||
appMap.put("company", app.getCompanyName());
|
||||
appMap.put("uninstallable", app.getUninstallable());
|
||||
appMap.put("description", app.getDescription());
|
||||
InputStream is = fileLoader.openStream(app.getApk());
|
||||
String downloadURL = fileDownloadConfig.getFileDownloadURL();
|
||||
if(downloadURL != null) {
|
||||
downloadURL = downloadURL.replace("{$id}", app.getApk().getId().toString());
|
||||
}
|
||||
appMap.put("download_url", downloadURL);
|
||||
|
||||
appMap.put("md5_checksum", md5Checksum(is));
|
||||
messageMap.put("app", appMap);
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.info("Try publish message to: {}", topicName);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
logger.info("Message published!");
|
||||
|
||||
// change log's last broadcast time
|
||||
log.setLastBroadcastTs(new Date());
|
||||
// add to commit context
|
||||
commitContext.addInstanceToCommit(log);
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error publish to device: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
lockService.unlock(log);
|
||||
}
|
||||
});
|
||||
dataManager.commit(commitContext);
|
||||
tx.commit();
|
||||
} finally {
|
||||
logger.info("Start broadcast download tasks DONE");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String updateTaskStatuses() throws Exception {
|
||||
String query = "select datas.arr[0] status,datas.arr[1] message from ("
|
||||
+ "select update_download_task_status() arr "
|
||||
+ ") datas";
|
||||
String[] params = new String[] {
|
||||
};
|
||||
logger.info("update_download_task_status()");
|
||||
int[] types = new int[] {
|
||||
};
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
String[] datas = runner.query(query,
|
||||
params,
|
||||
types,
|
||||
new ResultSetHandler<String[]>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String[] handle(ResultSet rs) throws SQLException {
|
||||
if(rs.next()) {
|
||||
String[] ret = new String[2];
|
||||
ret[0] = rs.getString(1);
|
||||
ret[1] = rs.getString(2);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(datas != null) {
|
||||
sb.append("[").append(datas[0]).append(", ").append(datas[1]).append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastChangeProfileTaskToDevices() throws Exception {
|
||||
logger.info("Broadcast Change Profile task ...");
|
||||
try {
|
||||
int qos = 2;
|
||||
String topicName = "SERVER_OUT";
|
||||
// generate json
|
||||
String reqId = broadcastConfig.getChangeProfileReqId();
|
||||
if(reqId == null) {
|
||||
reqId = UUID.randomUUID().toString();
|
||||
broadcastConfig.setChangeProfileReqId(reqId);
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
Map messageMap = new HashMap<>();
|
||||
messageMap.put("req_id", reqId);
|
||||
messageMap.put("req_time", dateTimeFormat.format(new Date()));
|
||||
messageMap.put("req_type", "CHANGE_PROFILE");
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.info("Try publish message to: {}", topicName);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
logger.info("Message published!");
|
||||
} finally {
|
||||
logger.info("Broadcast Change Profile task DONE");
|
||||
}
|
||||
}
|
||||
|
||||
private String md5Checksum(InputStream is) throws Exception {
|
||||
try {
|
||||
String checksum = DigestUtils.md5Hex(is);
|
||||
return checksum;
|
||||
} finally {
|
||||
if(is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.core.mqtt.MqttConnector;
|
||||
import com.google.gson.Gson;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service(EchoService.NAME)
|
||||
public class EchoServiceBean implements EchoService {
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private MqttConnector mqttConnector;
|
||||
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
// send to device topic via mqtt
|
||||
public void sendEcho() throws Exception {
|
||||
logger.info("Broadcast echo ...");
|
||||
try {
|
||||
int qos = 2;
|
||||
// self echo to SERVER_IN
|
||||
String topicName = "SERVER_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()));
|
||||
messageMap.put("req_type", "ECHO");
|
||||
|
||||
String messageData = gson.toJson(messageMap);
|
||||
|
||||
logger.info("Try publish message to: {}", topicName);
|
||||
//mqttConnector.publishRetainedMessage(topicName, messageData, qos);
|
||||
mqttConnector.publishMessage(topicName, messageData, qos);
|
||||
logger.info("Message published!");
|
||||
} catch(Exception ex) {
|
||||
logger.error("Error publish to device: {}", ex.getMessage(), ex);
|
||||
} finally {
|
||||
logger.info("Start broadcast echo task DONE");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,248 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.misc.FileDownloadWrapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.haulmont.cuba.core.app.FileStorageService;
|
||||
import com.haulmont.cuba.core.entity.FileDescriptor;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.app.Authentication;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Service(FileService.NAME)
|
||||
public class FileServiceBean implements FileService {
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
@Inject
|
||||
private FileStorageService fileStorageService;
|
||||
@Inject
|
||||
private FileLoader fileLoader;
|
||||
private Authentication authentication;
|
||||
|
||||
@Override
|
||||
public void downloadFile(@PathVariable String fileDescriptorId,
|
||||
HttpServletResponse response) {
|
||||
try {
|
||||
authentication.begin();
|
||||
UUID fileId = UUID.fromString(fileDescriptorId);
|
||||
LoadContext<FileDescriptor> loadContext = LoadContext.create(FileDescriptor.class);
|
||||
loadContext.setId(fileId);
|
||||
FileDescriptor fd = dataManager.load(loadContext);
|
||||
byte[] bytes = fileStorageService.loadFile(fd);
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
response.setDateHeader("Expires", 0);
|
||||
response.setHeader("Content-Type", getContentType(fd));
|
||||
response.setHeader("Content-Disposition", "attachment" + "; filename=\"" + fd.getName() + "\"");
|
||||
|
||||
downloadFromMiddlewareAndWriteResponse(fd, response);
|
||||
} catch(Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
authentication.end();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileDownloadWrapper uploadFileToRemote(FileDescriptor fd) {
|
||||
try {
|
||||
return uploadFile(fd);
|
||||
} catch(Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileDownloadWrapper uploadFileToRemote(FileDescriptor fd, boolean useFilename) {
|
||||
try {
|
||||
return uploadFile(fd, useFilename);
|
||||
} catch(Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
protected void downloadFromMiddlewareAndWriteResponse(FileDescriptor fd, HttpServletResponse response) throws IOException {
|
||||
ServletOutputStream os = response.getOutputStream();
|
||||
try (InputStream is = fileLoader.openStream(fd)) {
|
||||
IOUtils.copy(is, os);
|
||||
os.flush();
|
||||
} catch (FileStorageException e) {
|
||||
throw new IOException("Unable to download file from FileStorage: " + fd.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getContentType(FileDescriptor fd) {
|
||||
if (StringUtils.isEmpty(fd.getExtension())) {
|
||||
return FileTypesHelper.DEFAULT_MIME_TYPE;
|
||||
}
|
||||
|
||||
return FileTypesHelper.getMIMEType("." + fd.getExtension().toLowerCase());
|
||||
}
|
||||
|
||||
public FileDownloadWrapper uploadFile(FileDescriptor fd, boolean useFilename) throws Exception {
|
||||
try {
|
||||
int connectTimeout = 10;
|
||||
int timeout = 30;
|
||||
|
||||
FileDownloadWrapper fdWrapper = new FileDownloadWrapper();
|
||||
fdWrapper.setUploaded(false);
|
||||
|
||||
byte[] bytes = fileStorageService.loadFile(fd);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
// get md5 checksum
|
||||
String md5Checksum = md5Checksum(bais);
|
||||
|
||||
String url = "http://filedownload.unifiedtms.id:7177/apiEDC";
|
||||
RequestConfig config = RequestConfig.custom().setConnectTimeout(connectTimeout * 1000).setConnectionRequestTimeout(connectTimeout * 1000).setSocketTimeout(timeout * 1000).build();
|
||||
CloseableHttpClient httpclient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
|
||||
String filename = fd.getId()+"."+fd.getExtension();
|
||||
if(useFilename) {
|
||||
filename = fd.getName();
|
||||
}
|
||||
|
||||
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
|
||||
builder.addBinaryBody
|
||||
("document", bytes, ContentType.DEFAULT_BINARY, filename);
|
||||
builder.addTextBody("command", "upload-file-to-minio");
|
||||
HttpEntity reqEntity = builder.build();
|
||||
httpPost.setEntity(reqEntity);
|
||||
logger.info(">>> Upload file: {}", filename);
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
if (statusLine.getStatusCode() != 200) {
|
||||
throw new RuntimeException("HTTP Status Not 200 (OK) : " + statusLine.getStatusCode() + " - " + statusLine.getReasonPhrase());
|
||||
} else {
|
||||
HttpEntity entity = response.getEntity();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
entity.writeTo(baos);
|
||||
String jsonResponse = new String(baos.toByteArray());
|
||||
|
||||
logger.info("<<< {}", jsonResponse);
|
||||
|
||||
Gson gson = new Gson();
|
||||
Map responseMap = gson.fromJson(jsonResponse, Map.class);
|
||||
Double status = (Double) responseMap.get("status");
|
||||
if (status == 200) {
|
||||
// get file
|
||||
String downloadUrl = getFiledownloadUrl(filename);
|
||||
fdWrapper.setDownloadUrl(downloadUrl);
|
||||
fdWrapper.setUploaded(true);
|
||||
} else {
|
||||
String error = (String) responseMap.get("error");
|
||||
throw new RuntimeException("Error Upload File: " + error);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
httpclient.close();
|
||||
}
|
||||
fdWrapper.setChecksum(md5Checksum);
|
||||
return fdWrapper;
|
||||
} catch (Exception ex) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public FileDownloadWrapper uploadFile(FileDescriptor fd) throws Exception {
|
||||
return uploadFile(fd, false);
|
||||
}
|
||||
|
||||
public String getFiledownloadUrl(String filename) throws Exception {
|
||||
try {
|
||||
int connectTimeout = 10;
|
||||
int timeout = 30;
|
||||
|
||||
String downloadUrl = "null";
|
||||
|
||||
String url = "http://filedownload.unifiedtms.id:7177/apiEDC";
|
||||
RequestConfig config = RequestConfig.custom().setConnectTimeout(connectTimeout * 1000).setConnectionRequestTimeout(connectTimeout * 1000).setSocketTimeout(timeout * 1000).build();
|
||||
CloseableHttpClient httpclient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
|
||||
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
|
||||
builder.addTextBody("fileName", filename);
|
||||
builder.addTextBody("command", "get-file-from-minio");
|
||||
HttpEntity reqEntity = builder.build();
|
||||
httpPost.setEntity(reqEntity);
|
||||
logger.info(">>> Get file: {}", filename);
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
if (statusLine.getStatusCode() != 200) {
|
||||
throw new RuntimeException("HTTP Status Not 200 (OK) : " + statusLine.getStatusCode() + " - " + statusLine.getReasonPhrase());
|
||||
} else {
|
||||
HttpEntity entity = response.getEntity();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
entity.writeTo(baos);
|
||||
String jsonResponse = new String(baos.toByteArray());
|
||||
|
||||
logger.info("<<< {}", jsonResponse);
|
||||
|
||||
Gson gson = new Gson();
|
||||
Map responseMap = gson.fromJson(jsonResponse, Map.class);
|
||||
Double status = (Double) responseMap.get("status");
|
||||
if (status == 200) {
|
||||
// get file
|
||||
downloadUrl = (String) responseMap.get("url");
|
||||
} else {
|
||||
String message = (String) responseMap.get("message");
|
||||
throw new RuntimeException("Error Get File: " + message);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
httpclient.close();
|
||||
}
|
||||
return downloadUrl;
|
||||
} catch (Exception ex) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private String md5Checksum(InputStream is) throws Exception {
|
||||
try {
|
||||
String checksum = DigestUtils.md5Hex(is);
|
||||
return checksum;
|
||||
} finally {
|
||||
if(is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.restmodel.HeartBeatRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service(HeartBeatService.NAME)
|
||||
public class HeartBeatServiceBean implements HeartBeatService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(HeartBeatServiceBean.class);
|
||||
|
||||
@Override
|
||||
public boolean heartBeat(HeartBeatRequest heartBeatRequest) {
|
||||
logger.info("#heartBeat: " + heartBeatRequest);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.Singleton;
|
||||
import com.cmobile.unifiedtms.config.LicenseConfig;
|
||||
import com.cmobile.unifiedtms.entity.LicenseModel;
|
||||
import com.haulmont.cuba.core.app.FileStorageAPI;
|
||||
import com.haulmont.cuba.core.entity.FileDescriptor;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.license4j.License;
|
||||
import com.license4j.LicenseText;
|
||||
import com.license4j.LicenseValidator;
|
||||
import com.license4j.ValidationStatus;
|
||||
import com.license4j.util.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
@Service(LicenseService.NAME)
|
||||
public class LicenseServiceBean implements LicenseService {
|
||||
|
||||
private final static String LICENSE = "_LICENSE_";
|
||||
private final static String productEdition = "Production";
|
||||
private final static String productVersion = "1.0";
|
||||
private final static String productID = "utms";
|
||||
private final static String publicKey = "30820122300d06092a864886f70d01010105000382010f00303032301006072a8648ce3d02002EC311215SHA512withECDSA106052b81040006031e0004125ca10acdc54807553b11bb9b4f59fe947f68788fe9e6f0d324a03bG82010a0282010100859c388d100cacf014e7f61be9d30f5c262feadf8440affe93a85021e212a808ff6aa40e2d318ef6b298f61a68fc8bde2237fbd9241988fb7a6fe07138bab8ace340e907213daaeb7121747c5f022bd7ee5f49d49dba64883fa0674403RSA4204813SHA512withRSA7672242c571f7edb5966469150d9240464c64087727561c8cc40609b8ff0ea7e07bd08d643f6c04ef3c860092354be465bb3136cb517b0cbb5f5718f8fd95e95a33b848f01cec185afbb43062af5c8ff7f2030783371b6dbbd4f7230b1725b4dc846d1a40d7b38d369ee6df56d8a8648858b5f5b6aac61c12698dfac749bd112443bec8b9bc13e9c405053200789664afee8ed6b7dd52b264f1f58e6843bd18ae5eab76b0203010001";
|
||||
|
||||
@Inject
|
||||
private LicenseConfig licenseConfig;
|
||||
@Inject
|
||||
private Logger logger;
|
||||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
|
||||
private String lastLoadedLicenseString = null;
|
||||
private String lastLoadedLicenseDate = null;
|
||||
|
||||
@Override
|
||||
public LicenseModel validateLicense() throws Exception {
|
||||
try {
|
||||
String licenseString = licenseConfig.getLicenseString();
|
||||
if(licenseString == null) {
|
||||
throw new Exception("No License Provided!!");
|
||||
}
|
||||
String today = dateFormat.format(new Date());
|
||||
if(!licenseString.equals(lastLoadedLicenseString) || !lastLoadedLicenseDate.equals(today)) {
|
||||
lastLoadedLicenseString = licenseString;
|
||||
lastLoadedLicenseDate = dateFormat.format(new Date());
|
||||
// reloaded
|
||||
} else {
|
||||
// use already loaded license
|
||||
return (LicenseModel) Singleton.getInstance(LICENSE).getObject();
|
||||
}
|
||||
logger.info("License String: {}", licenseString);
|
||||
|
||||
License validLicense = LicenseValidator.validate(
|
||||
licenseString,
|
||||
publicKey,
|
||||
productID,
|
||||
productEdition,
|
||||
productVersion,
|
||||
null,
|
||||
null);
|
||||
// wrap into LicenseModel
|
||||
LicenseModel licenseModel = new LicenseModel();
|
||||
LicenseText lt = validLicense.getLicenseText();
|
||||
if(lt != null) {
|
||||
licenseModel.setLicenseID(lt.getLicenseID());
|
||||
licenseModel.setLicenseExpireDate(lt.getLicenseExpireDate());
|
||||
licenseModel.setLicenseHardwareID(lt.getLicenseHardwareID());
|
||||
licenseModel.setLicenseProductName(lt.getLicenseProductName());
|
||||
licenseModel.setLicenseSignature(lt.getLicenseSignature());
|
||||
licenseModel.setLicenseProperties(lt.getLicenseProperties());
|
||||
licenseModel.setLicenseValidProductEdition(lt.getLicenseValidProductEdition());
|
||||
licenseModel.setLicenseValidProductID(lt.getLicenseValidProductID());
|
||||
licenseModel.setLicenseValidProductVersion(lt.getLicenseValidProductVersion());
|
||||
licenseModel.setUserCity(lt.getUserCity());
|
||||
licenseModel.setUserCompany(lt.getUserCompany());
|
||||
licenseModel.setUserCity(lt.getUserCity());
|
||||
licenseModel.setUserCountry(lt.getUserCountry());
|
||||
licenseModel.setUserFax(lt.getUserFax());
|
||||
licenseModel.setUserEmail(lt.getUserEMail());
|
||||
licenseModel.setUserTelephone(lt.getUserTelephone());
|
||||
licenseModel.setUserFullname(lt.getUserFullName());
|
||||
licenseModel.setUserRegisteredTo(lt.getUserRegisteredTo());
|
||||
licenseModel.setUserStreet(lt.getUserStreet());
|
||||
licenseModel.setUserZipCode(lt.getUserZipCode());
|
||||
licenseModel.setLicenseGeneratedDateTime(new Date(lt.getLicenseGenerationDateTime()));
|
||||
}
|
||||
licenseModel.setValidationStatus(validLicense.getValidationStatus());
|
||||
// create singleton holding the loaded license
|
||||
Singleton.getInstance(LICENSE).setObject(licenseModel);
|
||||
return licenseModel;
|
||||
} finally {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LicenseModel validateLicense(String licenseString) throws Exception {
|
||||
try {
|
||||
License validLicense = LicenseValidator.validate(
|
||||
licenseString,
|
||||
publicKey,
|
||||
productID,
|
||||
productEdition,
|
||||
productVersion,
|
||||
null,
|
||||
null);
|
||||
// wrap into LicenseModel
|
||||
LicenseModel licenseModel = new LicenseModel();
|
||||
LicenseText lt = validLicense.getLicenseText();
|
||||
if(lt != null) {
|
||||
licenseModel.setLicenseID(lt.getLicenseID());
|
||||
licenseModel.setLicenseExpireDate(lt.getLicenseExpireDate());
|
||||
licenseModel.setLicenseHardwareID(lt.getLicenseHardwareID());
|
||||
licenseModel.setLicenseProductName(lt.getLicenseProductName());
|
||||
licenseModel.setLicenseSignature(lt.getLicenseSignature());
|
||||
licenseModel.setLicenseProperties(lt.getLicenseProperties());
|
||||
licenseModel.setLicenseValidProductEdition(lt.getLicenseValidProductEdition());
|
||||
licenseModel.setLicenseValidProductID(lt.getLicenseValidProductID());
|
||||
licenseModel.setLicenseValidProductVersion(lt.getLicenseValidProductVersion());
|
||||
licenseModel.setUserCity(lt.getUserCity());
|
||||
licenseModel.setUserCompany(lt.getUserCompany());
|
||||
licenseModel.setUserCity(lt.getUserCity());
|
||||
licenseModel.setUserCountry(lt.getUserCountry());
|
||||
licenseModel.setUserFax(lt.getUserFax());
|
||||
licenseModel.setUserEmail(lt.getUserEMail());
|
||||
licenseModel.setUserTelephone(lt.getUserTelephone());
|
||||
licenseModel.setUserFullname(lt.getUserFullName());
|
||||
licenseModel.setUserRegisteredTo(lt.getUserRegisteredTo());
|
||||
licenseModel.setUserStreet(lt.getUserStreet());
|
||||
licenseModel.setUserZipCode(lt.getUserZipCode());
|
||||
licenseModel.setLicenseGeneratedDateTime(new Date(lt.getLicenseGenerationDateTime()));
|
||||
}
|
||||
licenseModel.setValidationStatus(validLicense.getValidationStatus());
|
||||
return licenseModel;
|
||||
} finally {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationStatus installLicense(String licenseString) throws Exception {
|
||||
try {
|
||||
License validLicense = LicenseValidator.validate(
|
||||
licenseString,
|
||||
publicKey,
|
||||
productID,
|
||||
productEdition,
|
||||
productVersion,
|
||||
null,
|
||||
null);
|
||||
if(ValidationStatus.LICENSE_VALID == validLicense.getValidationStatus()) {
|
||||
licenseConfig.setLicenseString(licenseString);
|
||||
} else {
|
||||
// invalid license
|
||||
}
|
||||
return validLicense.getValidationStatus();
|
||||
} finally {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLicenseStringFromFile(FileDescriptor fileDescriptor) throws Exception {
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
byte[] licenseBytes = fileStorageAPI.loadFile(fileDescriptor);
|
||||
String licenseString = readFile(licenseBytes);
|
||||
return licenseString;
|
||||
}
|
||||
|
||||
private String readFile(byte[] bytes) throws IOException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes)));
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
String str;
|
||||
while ((str = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(str);
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
bufferedReader.close();
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.haulmont.addon.sdbmt.core.app.multitenancy.TenantProvider;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Service(NumericInfoWidgetService.NAME)
|
||||
public class NumericInfoWidgetServiceBean implements NumericInfoWidgetService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(NumericInfoWidgetServiceBean.class);
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
@Inject
|
||||
private TenantProvider tenantProvider;
|
||||
|
||||
@Override
|
||||
public Number getNumberFromQueryTemplate(String queryTemplate) {
|
||||
if("active-terminal".equals(queryTemplate)) {
|
||||
return getActiveTerminal();
|
||||
} else if("total-terminal".equals(queryTemplate)) {
|
||||
return getTotalTerminal();
|
||||
} else if("total-apps".equals(queryTemplate)) {
|
||||
return getTotalApps();
|
||||
} else if("download-task".equals(queryTemplate)) {
|
||||
return getTotalInProgressDownloadTask();
|
||||
} else if("total-download".equals(queryTemplate)) {
|
||||
return getTotalDownload();
|
||||
} else if("today-download".equals(queryTemplate)) {
|
||||
return getTotalTodayDownload();
|
||||
} else if("terminal-offline-1-hour".equals(queryTemplate)) {
|
||||
return getOfflineTerminal1Hour();
|
||||
} else if("terminal-offline-6-hours".equals(queryTemplate)) {
|
||||
return getOfflineTerminal6Hours();
|
||||
} else if("terminal-offline-12-hours".equals(queryTemplate)) {
|
||||
return getOfflineTerminal12Hours();
|
||||
} else if("terminal-offline-1-day".equals(queryTemplate)) {
|
||||
return getOfflineTerminal1Day();
|
||||
} else if("terminal-offline-1-week".equals(queryTemplate)) {
|
||||
return getOfflineTerminal1Week();
|
||||
} else if("terminal-offline-1-month".equals(queryTemplate)) {
|
||||
return getOfflineTerminal1Month();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Double _getValueFromQuery(String query) {
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
Set<Double> numbers = runner.query(query,
|
||||
new ResultSetHandler<Set<Double>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<Double> handle(ResultSet rs) throws SQLException {
|
||||
Set<Double> rows = new HashSet<Double>();
|
||||
while (rs.next()) {
|
||||
rows.add(rs.getDouble(1));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
});
|
||||
return numbers.isEmpty() ? null : numbers.toArray(new Double[] {})[0];
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Double _getValueFromQuery(String query, String tenantId) {
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
Set<Double> numbers = runner.query(query,
|
||||
new String[] { tenantId },
|
||||
new int[] { Types.VARCHAR },
|
||||
new ResultSetHandler<Set<Double>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Set<Double> handle(ResultSet rs) throws SQLException {
|
||||
Set<Double> rows = new HashSet<Double>();
|
||||
while (rs.next()) {
|
||||
rows.add(rs.getDouble(1));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
});
|
||||
return numbers.isEmpty() ? null : numbers.toArray(new Double[] {})[0];
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: filter only active terminal
|
||||
*/
|
||||
private Integer getActiveTerminal() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
logger.info("#getActiveTerminal: '" + tenantId + "'");
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery("select count(*) _value from ( select id from tms_terminal t where t.heartbeat_status=1 and t.delete_ts is null ) s");
|
||||
} else {
|
||||
value = _getValueFromQuery("select count(*) _value from ( select id from tms_terminal t where t.tenant_id=? and t.heartbeat_status=1 and t.delete_ts is null ) s", tenantId);
|
||||
}
|
||||
logger.info("--> value: '" + value + "'");
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getTotalTerminal() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery("select count(*) _value from ( select id from tms_terminal where delete_ts is null ) t");
|
||||
} else {
|
||||
value = _getValueFromQuery("select count(*) _value from ( select id from tms_terminal where tenant_id=? and delete_ts is null ) t", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getTotalApps() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery("select count(*) _value from tms_application where delete_ts is null");
|
||||
} else {
|
||||
value = _getValueFromQuery("select count(*) _value from tms_application where tenant_id=? and delete_ts is null", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getTotalInProgressDownloadTask() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery("select count(*) _value from tms_download_task where status = 2 and delete_ts is null");
|
||||
} else {
|
||||
value = _getValueFromQuery("select count(*) _value from tms_download_task where tenant_id=? and status = 2 and delete_ts is null", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getTotalDownload() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(distinct log.id) _value "
|
||||
+ "from tms_download_task task "
|
||||
+ " inner join tms_download_task_application_link link on link.download_task_id=task.id "
|
||||
+ " inner join tms_download_task_log log on log.task_id=task.id and log.application_id=link.application_id "
|
||||
+ "where task.delete_ts is null and log.activity <> 1");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(distinct log.id) _value "
|
||||
+ "from tms_download_task task "
|
||||
+ " inner join tms_download_task_application_link link on link.download_task_id=task.id "
|
||||
+ " inner join tms_download_task_log log on log.task_id=task.id and log.application_id=link.application_id "
|
||||
+ "where task.tenant_id = ? and task.delete_ts is null and log.activity <> 1", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getTotalTodayDownload() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(distinct log.id) _value "
|
||||
+ "from tms_download_task task "
|
||||
+ " inner join tms_download_task_application_link link on link.download_task_id=task.id "
|
||||
+ " inner join tms_download_task_log log on log.task_id=task.id and log.application_id=link.application_id "
|
||||
+ " and (task.create_ts::date = CURRENT_DATE or task.update_ts::date = CURRENT_DATE) "
|
||||
+ "where task.delete_ts is null and log.activity in (2,3,4,5,6,7)");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(distinct log.id) _value "
|
||||
+ "from tms_download_task task "
|
||||
+ " inner join tms_download_task_application_link link on link.download_task_id=task.id "
|
||||
+ " inner join tms_download_task_log log on log.task_id=task.id and log.application_id=link.application_id "
|
||||
+ " and (task.create_ts::date = CURRENT_DATE or task.update_ts::date = CURRENT_DATE) "
|
||||
+ "where task.tenant_id = ? and task.delete_ts is null and log.activity in (2,3,4,5,6,7)", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getOfflineTerminal1Hour() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 hour')");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 hour') and task.tenant_id", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getOfflineTerminal6Hours() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '6 hours')");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '6 hours') and task.tenant_id", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getOfflineTerminal12Hours() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '12 hours')");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '12 hours') and task.tenant_id", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getOfflineTerminal1Day() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 day')");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 day') and task.tenant_id", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getOfflineTerminal1Week() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 week')");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 week') and task.tenant_id", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
|
||||
private Integer getOfflineTerminal1Month() {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
Double value =null;
|
||||
if(tenantId == null || "no_tenant".equals(tenantId)) {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 month')");
|
||||
} else {
|
||||
value = _getValueFromQuery(""
|
||||
+ "select count(*) total "
|
||||
+ "from tms_terminal "
|
||||
+ "where delete_ts is null and last_heartbeat_time < (now() - interval '1 month') and task.tenant_id", tenantId);
|
||||
}
|
||||
return value != null ? value.intValue() : null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,118 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.ResponseCode;
|
||||
import com.cmobile.unifiedtms.entity.enums.TrxType;
|
||||
import com.cmobile.unifiedtms.ext.utils.XlsHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.cmobile.unifiedtms.ext.entity.Card;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.app.FileStorageAPI;
|
||||
import com.haulmont.cuba.core.entity.FileDescriptor;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import com.cmobile.unifiedtms.ext.exception.ImportFileEofEvaluationException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service(ResponseCodeService.NAME)
|
||||
public class ResponseCodeServiceBean implements ResponseCodeService {
|
||||
|
||||
protected static final char NON_BREAKING_SPACE = (char) 160;
|
||||
private Sheet sheet = null;
|
||||
private int sheetIndex = 0;
|
||||
|
||||
protected FileDescriptor fileDescriptor;
|
||||
protected Persistence persistence;
|
||||
protected Metadata metadata;
|
||||
protected TimeSource timeSource;
|
||||
|
||||
protected int firstDataRowIndex = 0;
|
||||
protected int dataRowIncrement = 1;
|
||||
protected Map<String, Integer> attributesToColumns;
|
||||
protected int currentRowIndex;
|
||||
|
||||
public static final String TRX_TYPE = "type";
|
||||
public static final String CODE = "code";
|
||||
public static final String DESCRIPTION = "description";
|
||||
|
||||
@Override
|
||||
public List<ResponseCode> parseResponseCodeFromFile(FileDescriptor fd) throws Exception {
|
||||
List<ResponseCode> list = new ArrayList<ResponseCode>();
|
||||
|
||||
{
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
byte[] xlsFile = fileStorageAPI.loadFile(fd);
|
||||
Workbook workbook = XlsHelper.openWorkbook(xlsFile);
|
||||
|
||||
if (workbook == null) {
|
||||
throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, "File was not loaded");
|
||||
}
|
||||
|
||||
sheet = workbook.getSheetAt(sheetIndex);
|
||||
persistence = AppBeans.get(Persistence.NAME);
|
||||
metadata = AppBeans.get(Metadata.NAME);
|
||||
timeSource = AppBeans.get(TimeSource.NAME);
|
||||
|
||||
attributesToColumns = createAttributesToColumns();
|
||||
|
||||
// read the values
|
||||
firstDataRowIndex = 1;
|
||||
|
||||
try {
|
||||
for (currentRowIndex = firstDataRowIndex; !eof(sheet.getRow(currentRowIndex)); currentRowIndex += dataRowIncrement) {
|
||||
Row row = sheet.getRow(currentRowIndex);
|
||||
|
||||
ResponseCode responseCode = metadata.create(ResponseCode.class);
|
||||
String trxType = (String) XlsHelper.getCellValue(row.getCell(attributesToColumns.get(TRX_TYPE)));
|
||||
String code = String.valueOf(XlsHelper.getCellValue(row.getCell(attributesToColumns.get(CODE))));
|
||||
String description = String.valueOf(XlsHelper.getCellValue(row.getCell(attributesToColumns.get(DESCRIPTION))));
|
||||
|
||||
responseCode.setType(TrxType.fromId(trxType));
|
||||
responseCode.setCode(code);
|
||||
responseCode.setDesc(description);
|
||||
|
||||
list.add(responseCode);
|
||||
}
|
||||
} catch (ImportFileEofEvaluationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private Boolean eofByColumnNullValue(Row row, String columnAlias) throws ImportFileEofEvaluationException {
|
||||
Integer columnNumber = attributesToColumns.get(columnAlias);
|
||||
if (row != null) {
|
||||
Cell cell = row.getCell(columnNumber);
|
||||
Object cellValue;
|
||||
try {
|
||||
cellValue = XlsHelper.getCellValue(cell);
|
||||
} catch (Exception e) {
|
||||
throw new ImportFileEofEvaluationException(String.format("Eof evaluation has failed on row %s", row.getRowNum()));
|
||||
}
|
||||
return cellValue == null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Map<String, Integer> createAttributesToColumns() {
|
||||
Map<String, Integer> columns = new HashMap<>();
|
||||
|
||||
columns.put(TRX_TYPE, 0);
|
||||
columns.put(CODE, 1);
|
||||
columns.put(DESCRIPTION, 2);
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
protected boolean eof(Row row) throws ImportFileEofEvaluationException {
|
||||
return eofByColumnNullValue(row, TRX_TYPE);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.haulmont.addon.sdbmt.core.app.multitenancy.TenantProvider;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
@Service(TerminalExtService.NAME)
|
||||
public class TerminalExtServiceBean implements TerminalExtService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(TerminalExtServiceBean.class);
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
@Inject
|
||||
private TenantProvider tenantProvider;
|
||||
|
||||
public String updateTerminalStatus() {
|
||||
String query = "select datas.arr[0] status,datas.arr[1] message from ("
|
||||
+ "select update_terminal_status() arr "
|
||||
+ ") datas";
|
||||
String[] params = new String[] {
|
||||
};
|
||||
logger.info("update_terminal_status()");
|
||||
int[] types = new int[] {
|
||||
};
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
String[] datas = runner.query(query,
|
||||
params,
|
||||
types,
|
||||
new ResultSetHandler<String[]>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String[] handle(ResultSet rs) throws SQLException {
|
||||
if(rs.next()) {
|
||||
String[] ret = new String[2];
|
||||
ret[0] = rs.getString(1);
|
||||
ret[1] = rs.getString(2);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(datas != null) {
|
||||
sb.append("[").append(datas[0]).append(", ").append(datas[1]).append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String removeTerminalByTid(String terminalId) {
|
||||
String query = "select datas.arr[0] status,datas.arr[1] message from ("
|
||||
+ "select remove_terminal_by_tid(?) arr "
|
||||
+ ") datas";
|
||||
String[] params = new String[] {
|
||||
terminalId
|
||||
};
|
||||
logger.info("remove_terminal_by_tid()");
|
||||
int[] types = new int[] {
|
||||
Types.VARCHAR,
|
||||
};
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
String[] datas = runner.query(query,
|
||||
params,
|
||||
types,
|
||||
new ResultSetHandler<String[]>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String[] handle(ResultSet rs) throws SQLException {
|
||||
if(rs.next()) {
|
||||
String[] ret = new String[2];
|
||||
ret[0] = rs.getString(1);
|
||||
ret[1] = rs.getString(2);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(datas != null) {
|
||||
sb.append("[").append(datas[0]).append(", ").append(datas[1]).append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,461 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.misc.TerminalGroupImportObjectWrapper;
|
||||
import com.cmobile.unifiedtms.exception.ImportFileEofEvaluationException;
|
||||
import com.cmobile.unifiedtms.ext.entity.TerminalExt;
|
||||
import com.cmobile.unifiedtms.utils.XlsHelper;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.app.FileStorageAPI;
|
||||
import com.haulmont.cuba.core.entity.FileDescriptor;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
|
||||
@Service(TerminalGroupImporterService.NAME)
|
||||
public class TerminalGroupImporterServiceBean implements TerminalGroupImporterService {
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
|
||||
private Log log = LogFactory.getLog(this.getClass());
|
||||
protected static final char NON_BREAKING_SPACE = (char) 160;
|
||||
|
||||
protected FileDescriptor fileDescriptor;
|
||||
protected Metadata metadata;
|
||||
|
||||
protected int firstDataRowIndex = 0;
|
||||
protected int dataRowIncrement = 1;
|
||||
protected Map<String, Integer> terminalAttributesToColumns;
|
||||
protected int currentRowIndex;
|
||||
|
||||
public static final String TID = "TID";
|
||||
public static final String SN = "SN";
|
||||
private static final int TERMINAL_LOOKUP_BATCH_SIZE = 500;
|
||||
private static final int TERMINAL_ATTACH_BATCH_SIZE = 1000;
|
||||
|
||||
@Override
|
||||
public List<TerminalGroupImportObjectWrapper> parseTerminalsFromFile(FileDescriptor fd) throws Exception {
|
||||
List<TerminalGroupImportObjectWrapper> terminals = new ArrayList<>();
|
||||
Workbook workbook = null;
|
||||
{
|
||||
long startTime = System.currentTimeMillis();
|
||||
logger.info("Terminal group import started. fileDescriptorId={}, fileName={}", fd.getId(), fd.getName());
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
long loadFileStartTime = System.currentTimeMillis();
|
||||
byte[] xlsFile = fileStorageAPI.loadFile(fd);
|
||||
logger.info("Terminal group import file loaded. size={} bytes, elapsed={} ms",
|
||||
xlsFile != null ? xlsFile.length : 0, System.currentTimeMillis() - loadFileStartTime);
|
||||
|
||||
long openWorkbookStartTime = System.currentTimeMillis();
|
||||
workbook = XlsHelper.openWorkbook(xlsFile);
|
||||
logger.info("Terminal group import workbook opened in {} ms",
|
||||
System.currentTimeMillis() - openWorkbookStartTime);
|
||||
|
||||
if (workbook == null) {
|
||||
throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, "File was not loaded");
|
||||
}
|
||||
Sheet terminalSheet = workbook.getSheetAt(0);
|
||||
logger.info("Terminal group import sheet selected. sheetName={}, lastRowNum={}",
|
||||
terminalSheet.getSheetName(), terminalSheet.getLastRowNum());
|
||||
|
||||
_parseTerminalsFromFile(workbook, terminalSheet, terminals);
|
||||
logger.info("Terminal group import finished. total={}, elapsed={} ms",
|
||||
terminals.size(), System.currentTimeMillis() - startTime);
|
||||
|
||||
}
|
||||
|
||||
return terminals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TerminalGroupImportObjectWrapper> parseTerminalsFromFileOptimized(FileDescriptor fd) throws Exception {
|
||||
List<TerminalGroupImportObjectWrapper> terminals = new ArrayList<>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
logger.info("Optimized terminal group import started. fileDescriptorId={}, fileName={}", fd.getId(), fd.getName());
|
||||
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
byte[] xlsFile = fileStorageAPI.loadFile(fd);
|
||||
Workbook workbook = XlsHelper.openWorkbook(xlsFile);
|
||||
if (workbook == null) {
|
||||
throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, "File was not loaded");
|
||||
}
|
||||
|
||||
Sheet terminalSheet = workbook.getSheetAt(0);
|
||||
List<TerminalGroupImportObjectWrapper> parsedTerminals = parseTerminalRows(terminalSheet);
|
||||
logger.info("Optimized terminal group import parsed {} rows. Matching terminals using import staging table ...",
|
||||
parsedTerminals.size());
|
||||
|
||||
matchTerminalsUsingImportStage(parsedTerminals);
|
||||
terminals.addAll(parsedTerminals);
|
||||
|
||||
logger.info("Optimized terminal group import finished. total={}, elapsed={} ms",
|
||||
terminals.size(), System.currentTimeMillis() - startTime);
|
||||
return terminals;
|
||||
}
|
||||
|
||||
private void _parseTerminalsFromFile(Workbook workbook, Sheet sheet, List<TerminalGroupImportObjectWrapper> terminals) throws Exception {
|
||||
{
|
||||
metadata = AppBeans.get(Metadata.NAME);
|
||||
terminalAttributesToColumns = createTerminalAttributesToColumns();
|
||||
|
||||
// read the values
|
||||
firstDataRowIndex = 1;
|
||||
|
||||
try {
|
||||
List<TerminalGroupImportObjectWrapper> parsedTerminals = new ArrayList<>();
|
||||
Set<String> serialNumbers = new LinkedHashSet<>();
|
||||
for (currentRowIndex = firstDataRowIndex; !eofTerminal(sheet.getRow(currentRowIndex)); currentRowIndex += dataRowIncrement) {
|
||||
Row row = sheet.getRow(currentRowIndex);
|
||||
if (currentRowIndex > firstDataRowIndex && currentRowIndex % 1000 == 0) {
|
||||
logger.info("Terminal group import reading row {}", currentRowIndex);
|
||||
}
|
||||
|
||||
String tid = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TID)));
|
||||
String sn = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(SN)));
|
||||
if(sn != null) {
|
||||
sn = sn.trim().toUpperCase();
|
||||
if (!sn.isEmpty()) {
|
||||
serialNumbers.add(sn);
|
||||
}
|
||||
}
|
||||
|
||||
TerminalGroupImportObjectWrapper terminalGroupImportObjectWrapper = new TerminalGroupImportObjectWrapper();
|
||||
terminalGroupImportObjectWrapper.setTid(tid);
|
||||
terminalGroupImportObjectWrapper.setSn(sn);
|
||||
parsedTerminals.add(terminalGroupImportObjectWrapper);
|
||||
}
|
||||
|
||||
logger.info("Terminal group import parsed {} rows with {} unique SNs. Loading terminals ...",
|
||||
parsedTerminals.size(), serialNumbers.size());
|
||||
long loadTerminalsStartTime = System.currentTimeMillis();
|
||||
Map<String, Terminal> terminalsBySn = loadTerminalsBySN(serialNumbers);
|
||||
logger.info("Terminal group import loaded {} matching terminals in {} ms",
|
||||
terminalsBySn.size(), System.currentTimeMillis() - loadTerminalsStartTime);
|
||||
for (TerminalGroupImportObjectWrapper terminalGroupImportObjectWrapper : parsedTerminals) {
|
||||
Terminal terminal = terminalGroupImportObjectWrapper.getSn() != null
|
||||
? terminalsBySn.get(terminalGroupImportObjectWrapper.getSn().toLowerCase())
|
||||
: null;
|
||||
terminalGroupImportObjectWrapper.setTerminal(terminal);
|
||||
if (terminal != null) {
|
||||
terminalGroupImportObjectWrapper.setSnFound(true);
|
||||
if(terminal.getTerminalLink() != null) {
|
||||
TerminalExt terminalExt = terminal.getTerminalLink().getTerminalExt();
|
||||
String terminalId = terminalExt != null ? terminalExt.getTerminalId() : null;
|
||||
String importedTid = terminalGroupImportObjectWrapper.getTid();
|
||||
terminalGroupImportObjectWrapper.setTidFound(terminalId != null
|
||||
&& importedTid != null
|
||||
&& terminalId.equalsIgnoreCase(importedTid));
|
||||
} else {
|
||||
terminalGroupImportObjectWrapper.setTidFound(false);
|
||||
}
|
||||
} else {
|
||||
terminalGroupImportObjectWrapper.setSnFound(false);
|
||||
terminalGroupImportObjectWrapper.setTidFound(false);
|
||||
}
|
||||
|
||||
// add to terminals
|
||||
terminals.add(terminalGroupImportObjectWrapper);
|
||||
}
|
||||
} catch (ImportFileEofEvaluationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Terminal> loadTerminalsBySN(Set<String> serialNumbers) {
|
||||
Map<String, Terminal> terminalsBySn = new HashMap<>();
|
||||
if (serialNumbers.isEmpty()) {
|
||||
return terminalsBySn;
|
||||
}
|
||||
|
||||
List<String> serialNumberList = new ArrayList<>(serialNumbers);
|
||||
for (int start = 0; start < serialNumberList.size(); start += TERMINAL_LOOKUP_BATCH_SIZE) {
|
||||
int end = Math.min(start + TERMINAL_LOOKUP_BATCH_SIZE, serialNumberList.size());
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where t.sn in :sns and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("sns", serialNumberList.subList(start, end));
|
||||
loadContext.setView("terminal-linked-view");
|
||||
|
||||
List<Terminal> batch = dataManager.loadList(loadContext);
|
||||
for (Terminal terminal : batch) {
|
||||
if (terminal.getSn() != null) {
|
||||
terminalsBySn.put(terminal.getSn().toLowerCase(), terminal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return terminalsBySn;
|
||||
}
|
||||
|
||||
private List<TerminalGroupImportObjectWrapper> parseTerminalRows(Sheet sheet) throws Exception {
|
||||
metadata = AppBeans.get(Metadata.NAME);
|
||||
terminalAttributesToColumns = createTerminalAttributesToColumns();
|
||||
firstDataRowIndex = 1;
|
||||
|
||||
List<TerminalGroupImportObjectWrapper> parsedTerminals = new ArrayList<>();
|
||||
for (currentRowIndex = firstDataRowIndex; !eofTerminal(sheet.getRow(currentRowIndex)); currentRowIndex += dataRowIncrement) {
|
||||
Row row = sheet.getRow(currentRowIndex);
|
||||
if (currentRowIndex > firstDataRowIndex && currentRowIndex % 1000 == 0) {
|
||||
logger.info("Terminal group import reading row {}", currentRowIndex);
|
||||
}
|
||||
|
||||
String tid = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TID)));
|
||||
String sn = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(SN)));
|
||||
if (sn != null) {
|
||||
sn = sn.trim().toUpperCase();
|
||||
}
|
||||
|
||||
TerminalGroupImportObjectWrapper terminalGroupImportObjectWrapper = new TerminalGroupImportObjectWrapper();
|
||||
terminalGroupImportObjectWrapper.setTid(tid);
|
||||
terminalGroupImportObjectWrapper.setSn(sn);
|
||||
parsedTerminals.add(terminalGroupImportObjectWrapper);
|
||||
}
|
||||
|
||||
return parsedTerminals;
|
||||
}
|
||||
|
||||
private void matchTerminalsUsingImportStage(List<TerminalGroupImportObjectWrapper> parsedTerminals) {
|
||||
if (parsedTerminals.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID importId = UUID.randomUUID();
|
||||
logger.info("Terminal group optimized import matching started. importId={}, rows={}", importId, parsedTerminals.size());
|
||||
Map<Integer, TerminalGroupImportObjectWrapper> wrappersByRowNo = new HashMap<>();
|
||||
Map<UUID, List<TerminalGroupImportObjectWrapper>> wrappersByTerminalId = new LinkedHashMap<>();
|
||||
|
||||
Connection connection = null;
|
||||
boolean originalAutoCommit = true;
|
||||
try {
|
||||
connection = persistence.getDataSource().getConnection();
|
||||
originalAutoCommit = connection.getAutoCommit();
|
||||
connection.setAutoCommit(false);
|
||||
|
||||
long cleanupStartTime = System.currentTimeMillis();
|
||||
cleanupStaleImportStageRows(connection);
|
||||
logger.info("Terminal group optimized import stale stage cleanup finished. importId={}, elapsed={} ms",
|
||||
importId, System.currentTimeMillis() - cleanupStartTime);
|
||||
|
||||
long insertStartTime = System.currentTimeMillis();
|
||||
insertImportStageRows(connection, importId, parsedTerminals, wrappersByRowNo);
|
||||
logger.info("Terminal group optimized import stage insert finished. importId={}, rows={}, elapsed={} ms",
|
||||
importId, parsedTerminals.size(), System.currentTimeMillis() - insertStartTime);
|
||||
|
||||
long queryStartTime = System.currentTimeMillis();
|
||||
queryImportStageMatches(connection, importId, wrappersByRowNo, wrappersByTerminalId);
|
||||
logger.info("Terminal group optimized import match query finished. importId={}, matchedTerminalIds={}, elapsed={} ms",
|
||||
importId, wrappersByTerminalId.size(), System.currentTimeMillis() - queryStartTime);
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
rollbackQuietly(connection);
|
||||
throw new RuntimeException("Error matching terminal group import data using import staging table", e);
|
||||
} finally {
|
||||
cleanupImportStageRows(connection, importId);
|
||||
restoreAutoCommitQuietly(connection, originalAutoCommit);
|
||||
closeQuietly(connection);
|
||||
}
|
||||
|
||||
long attachStartTime = System.currentTimeMillis();
|
||||
attachMatchedTerminals(wrappersByTerminalId);
|
||||
logger.info("Terminal group optimized import matching finished. importId={}, elapsed={} ms",
|
||||
importId, System.currentTimeMillis() - attachStartTime);
|
||||
}
|
||||
|
||||
private void cleanupStaleImportStageRows(Connection connection) throws SQLException {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.executeUpdate("delete from tms_terminal_group_import_stage " +
|
||||
"where create_ts < current_timestamp - interval '1 day'");
|
||||
}
|
||||
}
|
||||
|
||||
private void insertImportStageRows(Connection connection,
|
||||
UUID importId,
|
||||
List<TerminalGroupImportObjectWrapper> parsedTerminals,
|
||||
Map<Integer, TerminalGroupImportObjectWrapper> wrappersByRowNo) throws SQLException {
|
||||
String sql = "insert into tms_terminal_group_import_stage " +
|
||||
"(import_id, row_no, tid, sn, tid_norm, sn_norm, sn_lower_norm) values (?, ?, ?, ?, ?, ?, ?)";
|
||||
try (PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
int rowNo = 0;
|
||||
for (TerminalGroupImportObjectWrapper wrapper : parsedTerminals) {
|
||||
rowNo++;
|
||||
wrappersByRowNo.put(rowNo, wrapper);
|
||||
|
||||
statement.setObject(1, importId);
|
||||
statement.setInt(2, rowNo);
|
||||
statement.setString(3, wrapper.getTid());
|
||||
statement.setString(4, wrapper.getSn());
|
||||
statement.setString(5, normalizeImportValue(wrapper.getTid()));
|
||||
statement.setString(6, normalizeImportValue(wrapper.getSn()));
|
||||
statement.setString(7, normalizeImportValueLower(wrapper.getSn()));
|
||||
statement.addBatch();
|
||||
|
||||
if (rowNo % 1000 == 0) {
|
||||
statement.executeBatch();
|
||||
}
|
||||
}
|
||||
statement.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
private void queryImportStageMatches(Connection connection,
|
||||
UUID importId,
|
||||
Map<Integer, TerminalGroupImportObjectWrapper> wrappersByRowNo,
|
||||
Map<UUID, List<TerminalGroupImportObjectWrapper>> wrappersByTerminalId) throws SQLException {
|
||||
String sql = "select i.row_no, t.id, te.terminal_id " +
|
||||
"from tms_terminal_group_import_stage i " +
|
||||
"left join tms_terminal t on t.sn in (i.sn, i.sn_norm, i.sn_lower_norm) and t.delete_ts is null " +
|
||||
"left join tms_terminal_link tl on tl.terminal_id = t.id " +
|
||||
"left join tmsext_terminal_ext te on te.id = tl.terminal_ext_id and te.delete_ts is null " +
|
||||
"where i.import_id = ? " +
|
||||
"order by i.row_no";
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
statement.setObject(1, importId);
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
int rowNo = resultSet.getInt(1);
|
||||
UUID terminalId = (UUID) resultSet.getObject(2);
|
||||
String terminalExtTid = resultSet.getString(3);
|
||||
TerminalGroupImportObjectWrapper wrapper = wrappersByRowNo.get(rowNo);
|
||||
if (wrapper == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean snFound = terminalId != null;
|
||||
wrapper.setSnFound(snFound);
|
||||
wrapper.setTidFound(snFound
|
||||
&& terminalExtTid != null
|
||||
&& wrapper.getTid() != null
|
||||
&& normalizeImportValue(terminalExtTid).equals(normalizeImportValue(wrapper.getTid())));
|
||||
|
||||
if (snFound) {
|
||||
wrappersByTerminalId.computeIfAbsent(terminalId, id -> new ArrayList<>()).add(wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupImportStageRows(Connection connection, UUID importId) {
|
||||
if (connection == null || importId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"delete from tms_terminal_group_import_stage where import_id = ?")) {
|
||||
if (connection.getAutoCommit()) {
|
||||
statement.setObject(1, importId);
|
||||
statement.executeUpdate();
|
||||
} else {
|
||||
statement.setObject(1, importId);
|
||||
statement.executeUpdate();
|
||||
connection.commit();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Unable to cleanup terminal group import staging rows. importId={}", importId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void attachMatchedTerminals(Map<UUID, List<TerminalGroupImportObjectWrapper>> wrappersByTerminalId) {
|
||||
if (wrappersByTerminalId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<UUID> terminalIds = new ArrayList<>(wrappersByTerminalId.keySet());
|
||||
for (int start = 0; start < terminalIds.size(); start += TERMINAL_ATTACH_BATCH_SIZE) {
|
||||
int end = Math.min(start + TERMINAL_ATTACH_BATCH_SIZE, terminalIds.size());
|
||||
LoadContext<Terminal> loadContext = LoadContext.create(Terminal.class);
|
||||
loadContext.setQueryString("select t from tms_Terminal t where t.id in :ids and t.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("ids", terminalIds.subList(start, end));
|
||||
loadContext.setView("terminal-linked-view");
|
||||
|
||||
List<Terminal> terminals = dataManager.loadList(loadContext);
|
||||
for (Terminal terminal : terminals) {
|
||||
List<TerminalGroupImportObjectWrapper> wrappers = wrappersByTerminalId.get(terminal.getId());
|
||||
if (wrappers != null) {
|
||||
for (TerminalGroupImportObjectWrapper wrapper : wrappers) {
|
||||
wrapper.setTerminal(terminal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeImportValue(String value) {
|
||||
return value != null ? value.trim().toUpperCase() : null;
|
||||
}
|
||||
|
||||
private String normalizeImportValueLower(String value) {
|
||||
return value != null ? value.trim().toLowerCase() : null;
|
||||
}
|
||||
|
||||
private void rollbackQuietly(Connection connection) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.rollback();
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreAutoCommitQuietly(Connection connection, boolean autoCommit) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.setAutoCommit(autoCommit);
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeQuietly(Connection connection) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean eofByColumnNullValueTerminal(Row row, String columnAlias) throws ImportFileEofEvaluationException {
|
||||
Integer columnNumber = terminalAttributesToColumns.get(columnAlias);
|
||||
if (row != null) {
|
||||
Cell cell = row.getCell(columnNumber);
|
||||
Object cellValue;
|
||||
try {
|
||||
cellValue = XlsHelper.getCellValue(cell);
|
||||
} catch (Exception e) {
|
||||
throw new ImportFileEofEvaluationException(String.format("Eof evaluation has failed on row %s", row.getRowNum()));
|
||||
}
|
||||
return cellValue == null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Map<String, Integer> createTerminalAttributesToColumns() {
|
||||
Map<String, Integer> columns = new HashMap<>();
|
||||
|
||||
columns.put(TID, 0);
|
||||
columns.put(SN, 1);
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
protected boolean eofTerminal(Row row) throws ImportFileEofEvaluationException {
|
||||
return eofByColumnNullValueTerminal(row, TID);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,811 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.*;
|
||||
import com.cmobile.unifiedtms.entity.chartdata.PieChartData;
|
||||
import com.cmobile.unifiedtms.entity.misc.TerminalImporterObjectWrapper;
|
||||
import com.cmobile.unifiedtms.exception.ImportFileEofEvaluationException;
|
||||
import com.cmobile.unifiedtms.ext.entity.Acquirer;
|
||||
import com.cmobile.unifiedtms.ext.entity.Card;
|
||||
import com.cmobile.unifiedtms.ext.entity.Issuer;
|
||||
import com.cmobile.unifiedtms.ext.entity.TerminalExt;
|
||||
import com.cmobile.unifiedtms.entity.TerminalImportBean;
|
||||
import com.cmobile.unifiedtms.utils.XlsHelper;
|
||||
import com.haulmont.addon.sdbmt.core.app.multitenancy.TenantProvider;
|
||||
import com.haulmont.bali.db.QueryRunner;
|
||||
import com.haulmont.bali.db.ResultSetHandler;
|
||||
import com.haulmont.cuba.core.Persistence;
|
||||
import com.haulmont.cuba.core.Transaction;
|
||||
import com.haulmont.cuba.core.app.FileStorageAPI;
|
||||
import com.haulmont.cuba.core.entity.FileDescriptor;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.poi.ss.SpreadsheetVersion;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Service(TerminalImporterService.NAME)
|
||||
public class TerminalImporterServiceBean implements TerminalImporterService {
|
||||
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
@Inject
|
||||
private MetadataTools metadataTools;
|
||||
@Inject
|
||||
private UuidSource uuidSource;
|
||||
@Inject
|
||||
private Persistence persistence;
|
||||
@Inject
|
||||
private UserSessionSource userSessionSource;
|
||||
@Inject
|
||||
private TenantProvider tenantProvider;
|
||||
@Inject
|
||||
private Logger logger;
|
||||
|
||||
private Log log = LogFactory.getLog(this.getClass());
|
||||
protected static final char NON_BREAKING_SPACE = (char) 160;
|
||||
|
||||
protected FileDescriptor fileDescriptor;
|
||||
protected Metadata metadata;
|
||||
|
||||
protected int firstDataRowIndex = 0;
|
||||
protected int dataRowIncrement = 1;
|
||||
protected Map<String, Integer> terminalAttributesToColumns;
|
||||
protected Map<String, Integer> terminalEditAttributesToColumns;
|
||||
protected int currentRowIndex;
|
||||
|
||||
public static final String SN = "SN";
|
||||
public static final String IMEI = "IMEI";
|
||||
public static final String MODEL_NAME = "ModelName";
|
||||
public static final String MERCHANT_NAME = "MerchantName";
|
||||
public static final String PROFILE_NAME = "ProfileName";
|
||||
public static final String GROUP_NAMES = "GroupNames";
|
||||
public static final String TEMPLATE_TERMINAL_ID = "TemplateTerminalID";
|
||||
public static final String TERMINAL_ID = "TerminalID";
|
||||
public static final String MERCHANT_ID = "MerchantID";
|
||||
public static final String MERCHANT_NAME1 = "MerchantName1";
|
||||
public static final String MERCHANT_NAME2 = "MerchantName2";
|
||||
public static final String MERCHANT_NAME3 = "MerchantName3";
|
||||
public static final String MERCHANT_NAME4 = "MerchantName4";
|
||||
public static final String MERCHANT_NAME5 = "MerchantName5";
|
||||
|
||||
// local caches
|
||||
private Map<UUID, List<Card>> issuerCards = new HashMap<>();
|
||||
private Map<UUID, UUID> issuerIds = new HashMap<>();
|
||||
private Map<String, TerminalExt> terminalTemplateCaches = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public TerminalImporterObjectWrapper parseTerminalsFromFile(FileDescriptor fd) throws Exception {
|
||||
List<TerminalLink> terminalLinks = new ArrayList<>();
|
||||
{
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
byte[] xlsFile = fileStorageAPI.loadFile(fd);
|
||||
Workbook workbook = XlsHelper.openWorkbook(xlsFile);
|
||||
|
||||
if (workbook == null) {
|
||||
throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, "File was not loaded");
|
||||
}
|
||||
Sheet terminalSheet = workbook.getSheetAt(0);
|
||||
|
||||
issuerIds.clear();
|
||||
issuerCards.clear();
|
||||
terminalTemplateCaches.clear();
|
||||
|
||||
_parseTerminalsFromFile(workbook, terminalSheet, terminalLinks);
|
||||
|
||||
// save the data
|
||||
//saveTerminals(terminalLinks);
|
||||
}
|
||||
|
||||
return new TerminalImporterObjectWrapper(terminalLinks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TerminalExt> parseTerminalsFromEditFile(FileDescriptor fd) throws Exception {
|
||||
List<TerminalExt> terminals = new ArrayList<>();
|
||||
{
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
byte[] xlsFile = fileStorageAPI.loadFile(fd);
|
||||
Workbook workbook = XlsHelper.openWorkbook(xlsFile);
|
||||
|
||||
if (workbook == null) {
|
||||
throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, "File was not loaded");
|
||||
}
|
||||
Sheet terminalSheet = workbook.getSheetAt(0);
|
||||
|
||||
_parseTerminalsFromEditFile(workbook, terminalSheet, terminals);
|
||||
}
|
||||
|
||||
return terminals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveTerminal(TerminalLink terminalLink) throws Exception {
|
||||
log.info("Saving terminal link ...");
|
||||
/*
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
// Terminal
|
||||
Terminal terminal = terminalLink.getTerminal();
|
||||
// Terminal Ext
|
||||
TerminalExt terminalExt = terminalLink.getTerminalExt();
|
||||
|
||||
// Order of saving
|
||||
// 1. Terminal
|
||||
// 2. Terminal Ext
|
||||
// 3. Acquirers
|
||||
// 4. Issuers + Link Card
|
||||
commitContext.addInstanceToCommit(terminal);
|
||||
commitContext.addInstanceToCommit(terminalExt);
|
||||
terminalExt.getAcquirers().forEach(acquirer -> {
|
||||
log.info(">>>> Acquirer#id: " + acquirer.getId() + " (" + acquirer.getName() + ")");
|
||||
acquirer.setTerminalExt(terminalExt);
|
||||
commitContext.addInstanceToCommit(acquirer);
|
||||
|
||||
acquirer.getIssuers().forEach(issuer -> {
|
||||
issuer.setAcquirer(acquirer);
|
||||
log.info(">>>> Issuer#id: " + issuer.getId() + " (" + issuer.getName() + ")");
|
||||
commitContext.addInstanceToCommit(issuer);
|
||||
});
|
||||
});
|
||||
commitContext.addInstanceToCommit(terminalLink);
|
||||
dataManager.commit(commitContext);
|
||||
commitContext.getCommitInstances().clear();
|
||||
|
||||
// last one save cards
|
||||
LoadContext<Issuer> issLoadContext = LoadContext.create(Issuer.class);
|
||||
terminalExt.getAcquirers().forEach(acquirer -> {
|
||||
acquirer.getIssuers().forEach(_issuer -> {
|
||||
issLoadContext.setId(_issuer.getId());
|
||||
issLoadContext.setView("issuer-view");
|
||||
Issuer issuer = dataManager.load(issLoadContext);
|
||||
UUID oldIssuerId = issuerIds.get(_issuer.getId());
|
||||
List<Card> oldCards = issuerCards.get(oldIssuerId);
|
||||
List<Card> cards = new ArrayList<>();
|
||||
for(Card _card : oldCards) {
|
||||
cards.add(_card);
|
||||
}
|
||||
issuer.setCards(cards);
|
||||
System.out.println("TOTAL CARDS: " + issuer.getCards().size());
|
||||
dataManager.commit(issuer);
|
||||
});
|
||||
});
|
||||
|
||||
tx.commit();
|
||||
return true;
|
||||
} catch(Exception ex) {
|
||||
throw ex;
|
||||
} finally {
|
||||
log.info("Saving terminal link DONE");
|
||||
}*/
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TerminalImportBean> parseTerminalsFromFile2(FileDescriptor fd) throws Exception {
|
||||
List<TerminalImportBean> terminals = new ArrayList<>();
|
||||
{
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
byte[] xlsFile = fileStorageAPI.loadFile(fd);
|
||||
Workbook workbook = XlsHelper.openWorkbook(xlsFile);
|
||||
|
||||
if (workbook == null) {
|
||||
throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, "File was not loaded");
|
||||
}
|
||||
Sheet terminalSheet = workbook.getSheetAt(0);
|
||||
_parseTerminalsFromFile2(workbook, terminalSheet, terminals);
|
||||
}
|
||||
|
||||
return terminals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TerminalProfilingItem> parseProfilingItems(FileDescriptor fd) throws Exception {
|
||||
List<TerminalProfilingItem> items = new ArrayList<>();
|
||||
{
|
||||
FileStorageAPI fileStorageAPI = AppBeans.get(FileStorageAPI.NAME);
|
||||
byte[] xlsFile = fileStorageAPI.loadFile(fd);
|
||||
Workbook workbook = XlsHelper.openWorkbook(xlsFile);
|
||||
|
||||
if (workbook == null) {
|
||||
throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, "File was not loaded");
|
||||
}
|
||||
Sheet terminalSheet = workbook.getSheetAt(0);
|
||||
_parseProfilingItemsFromFile(workbook, terminalSheet, items);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] saveTerminal(TerminalImportBean terminal) throws Exception {
|
||||
String tenantId = tenantProvider.getCurrentUserTenantId();
|
||||
if("no_tenant".equals(tenantId)) {
|
||||
tenantId = null;
|
||||
}
|
||||
String query = "select datas.arr[0] status,datas.arr[1] message from ("
|
||||
+ "select profiling_terminal(?,?,?,?,?,?,?,?,?,?,?,?,?,?) arr "
|
||||
+ ") datas";
|
||||
String[] params = new String[] {
|
||||
terminal.getSn(),
|
||||
terminal.getImei(),
|
||||
terminal.getModelName(),
|
||||
terminal.getMerchantName(),
|
||||
terminal.getProfileName(),
|
||||
terminal.getGroupNames(),
|
||||
terminal.getTemplateTerminalId(),
|
||||
terminal.getTerminalId(),
|
||||
terminal.getMerchantId(),
|
||||
terminal.getMerchantName1(),
|
||||
terminal.getMerchantName2(),
|
||||
terminal.getMerchantName3(),
|
||||
userSessionSource.getUserSession().getUser().getLoginLowerCase(),
|
||||
tenantId
|
||||
};
|
||||
logger.info("terminal_profiling({},{},{},{},{},{},{},{},{},{},{},{},{},{})",
|
||||
terminal.getSn(),
|
||||
terminal.getImei(),
|
||||
terminal.getModelName(),
|
||||
terminal.getMerchantName(),
|
||||
terminal.getProfileName(),
|
||||
terminal.getGroupNames(),
|
||||
terminal.getTemplateTerminalId(),
|
||||
terminal.getTerminalId(),
|
||||
terminal.getMerchantId(),
|
||||
terminal.getMerchantName1(),
|
||||
terminal.getMerchantName2(),
|
||||
terminal.getMerchantName3(),
|
||||
userSessionSource.getUserSession().getUser().getLoginLowerCase(),
|
||||
tenantId);
|
||||
int[] types = new int[] {
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR,
|
||||
Types.VARCHAR
|
||||
};
|
||||
QueryRunner runner = new QueryRunner(persistence.getDataSource());
|
||||
try {
|
||||
String[] datas = runner.query(query,
|
||||
params,
|
||||
types,
|
||||
new ResultSetHandler<String[]>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String[] handle(ResultSet rs) throws SQLException {
|
||||
if(rs.next()) {
|
||||
String[] ret = new String[2];
|
||||
ret[0] = rs.getString(1);
|
||||
ret[1] = rs.getString(2);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return datas;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTerminal(TerminalExt terminal) throws Exception {
|
||||
log.info("Saving terminal ...");
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
dataManager.commit(terminal);
|
||||
tx.commit();
|
||||
return true;
|
||||
} catch(Exception ex) {
|
||||
throw ex;
|
||||
} finally {
|
||||
log.info("Saving terminal DONE");
|
||||
}
|
||||
}
|
||||
|
||||
private void _parseTerminalsFromFile2(Workbook workbook, Sheet sheet, List<TerminalImportBean> terminals) throws Exception {
|
||||
{
|
||||
metadata = AppBeans.get(Metadata.NAME);
|
||||
terminalAttributesToColumns = createTerminalAttributesToColumns();
|
||||
|
||||
// read the values
|
||||
firstDataRowIndex = 2;
|
||||
|
||||
try {
|
||||
for (currentRowIndex = firstDataRowIndex; !eofTerminal(sheet.getRow(currentRowIndex)); currentRowIndex += dataRowIncrement) {
|
||||
Row row = sheet.getRow(currentRowIndex);
|
||||
|
||||
String sn = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(SN)));
|
||||
if(sn != null) {
|
||||
sn = sn.toUpperCase();
|
||||
}
|
||||
String imei = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(IMEI))));
|
||||
if(imei != null) {
|
||||
imei = imei.toUpperCase();
|
||||
}
|
||||
String modelName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MODEL_NAME))));
|
||||
String merchantName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME))));
|
||||
String profileName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(PROFILE_NAME))));
|
||||
String _groupNames = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(GROUP_NAMES))));
|
||||
String templateTerminalId = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TEMPLATE_TERMINAL_ID))));
|
||||
|
||||
String terminalId = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TERMINAL_ID)));
|
||||
String merchantId = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_ID)));
|
||||
String merchantName1 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME1))));
|
||||
String merchantName2 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME2))));
|
||||
String merchantName3 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME3))));
|
||||
String merchantName4 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME4))));
|
||||
String merchantName5 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME5))));
|
||||
|
||||
TerminalImportBean terminal = new TerminalImportBean();
|
||||
terminal.setSn(sn);
|
||||
terminal.setImei(imei);
|
||||
terminal.setModelName(modelName);
|
||||
terminal.setMerchantName(merchantName);
|
||||
terminal.setProfileName(profileName);
|
||||
terminal.setGroupNames(_groupNames);
|
||||
terminal.setTemplateTerminalId(templateTerminalId);
|
||||
terminal.setTerminalId(terminalId);
|
||||
terminal.setMerchantId(merchantId);
|
||||
terminal.setMerchantName1(merchantName1);
|
||||
terminal.setMerchantName2(merchantName2);
|
||||
terminal.setMerchantName3(merchantName3);
|
||||
terminal.setMerchantName4(merchantName4);
|
||||
terminal.setMerchantName5(merchantName5);
|
||||
|
||||
// add to terminal links
|
||||
terminals.add(terminal);
|
||||
}
|
||||
} catch (ImportFileEofEvaluationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void _parseProfilingItemsFromFile(Workbook workbook, Sheet sheet, List<TerminalProfilingItem> items) throws Exception {
|
||||
{
|
||||
metadata = AppBeans.get(Metadata.NAME);
|
||||
terminalAttributesToColumns = createTerminalAttributesToColumns();
|
||||
|
||||
// read the values
|
||||
firstDataRowIndex = 2;
|
||||
|
||||
try {
|
||||
for (currentRowIndex = firstDataRowIndex; !eofTerminal(sheet.getRow(currentRowIndex)); currentRowIndex += dataRowIncrement) {
|
||||
Row row = sheet.getRow(currentRowIndex);
|
||||
|
||||
String sn = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(SN)));
|
||||
if(sn != null) {
|
||||
sn = sn.toUpperCase();
|
||||
}
|
||||
String imei = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(IMEI))));
|
||||
if(imei != null) {
|
||||
imei = imei.toUpperCase();
|
||||
}
|
||||
String modelName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MODEL_NAME))));
|
||||
String merchantName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME))));
|
||||
String profileName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(PROFILE_NAME))));
|
||||
String _groupNames = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(GROUP_NAMES))));
|
||||
String templateTerminalId = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TEMPLATE_TERMINAL_ID))));
|
||||
|
||||
String terminalId = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TERMINAL_ID)));
|
||||
String merchantId = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_ID)));
|
||||
String merchantName1 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME1))));
|
||||
String merchantName2 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME2))));
|
||||
String merchantName3 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME3))));
|
||||
|
||||
TerminalProfilingItem item = new TerminalProfilingItem();
|
||||
item.setSn(sn);
|
||||
item.setImei(imei);
|
||||
item.setModelName(modelName);
|
||||
item.setMerchantName(merchantName);
|
||||
item.setProfileName(profileName);
|
||||
item.setGroupNames(_groupNames);
|
||||
item.setTerminalIdTemplate(templateTerminalId);
|
||||
item.setTerminalId(terminalId);
|
||||
item.setMerchantId(merchantId);
|
||||
item.setMerchantName1(merchantName1);
|
||||
item.setMerchantName2(merchantName2);
|
||||
item.setMerchantName3(merchantName3);
|
||||
|
||||
// add to item links
|
||||
items.add(item);
|
||||
}
|
||||
} catch (ImportFileEofEvaluationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _parseTerminalsFromFile(Workbook workbook, Sheet sheet, List<TerminalLink> terminalLinks) throws Exception {
|
||||
{
|
||||
/*
|
||||
metadata = AppBeans.get(Metadata.NAME);
|
||||
terminalAttributesToColumns = createTerminalAttributesToColumns();
|
||||
|
||||
// read the values
|
||||
firstDataRowIndex = 2;
|
||||
|
||||
try {
|
||||
for (currentRowIndex = firstDataRowIndex; !eofTerminal(sheet.getRow(currentRowIndex)); currentRowIndex += dataRowIncrement) {
|
||||
Row row = sheet.getRow(currentRowIndex);
|
||||
|
||||
String sn = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(SN)));
|
||||
if(sn != null) {
|
||||
sn = sn.toUpperCase();
|
||||
}
|
||||
String imei = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(IMEI))));
|
||||
if(imei != null) {
|
||||
imei = imei.toUpperCase();
|
||||
}
|
||||
String modelName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MODEL_NAME))));
|
||||
String merchantName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME))));
|
||||
String profileName = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(PROFILE_NAME))));
|
||||
String _groupNames = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(GROUP_NAMES))));
|
||||
String[] groupNames = new String[] {};
|
||||
if(_groupNames != null && !"".equals(_groupNames)) {
|
||||
groupNames = _groupNames.split(";");
|
||||
}
|
||||
String templateTerminalId = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TEMPLATE_TERMINAL_ID))));
|
||||
|
||||
String terminalId = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(TERMINAL_ID)));
|
||||
String merchantId = (String) XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_ID)));
|
||||
String merchantName1 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME1))));
|
||||
String merchantName2 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME2))));
|
||||
String merchantName3 = (String) (XlsHelper.getCellValue(row.getCell(terminalAttributesToColumns.get(MERCHANT_NAME3))));
|
||||
|
||||
// fetch model
|
||||
DeviceModel model = loadDeviceModelByName(modelName);
|
||||
// fetch merchant
|
||||
Merchant merchant = loadMerchantByName(merchantName);
|
||||
// fetch profile
|
||||
DeviceProfile profile = loadDeviceProfileByName(profileName);
|
||||
// fetch groups
|
||||
List<TerminalGroup> groups = new ArrayList<>();
|
||||
for(String groupName : groupNames) {
|
||||
if("".equals(groupName.trim())) continue;
|
||||
groups.add(loadTerminalGroupByName(groupName.trim()));
|
||||
}
|
||||
// terminal template
|
||||
log.info("Template Terminal ID: " + templateTerminalId);
|
||||
TerminalExt templateTerminal = terminalTemplateCaches.get(templateTerminalId);
|
||||
if(templateTerminal == null) {
|
||||
templateTerminal = loadTerminalExtByTerminalId(templateTerminalId);
|
||||
terminalTemplateCaches.put(templateTerminalId, templateTerminal);
|
||||
}
|
||||
log.info("Template Terminal: " + templateTerminal);
|
||||
List<Acquirer> templateAcquirers = templateTerminal.getAcquirers();
|
||||
|
||||
// cache of issuer's cards
|
||||
LoadContext<Card> loadContext = LoadContext.create(Card.class);
|
||||
LoadContext<Issuer> issLoadContext = LoadContext.create(Issuer.class);
|
||||
for(Acquirer oldAcquirer : templateTerminal.getAcquirers()) {
|
||||
for(Issuer _oldIssuer : oldAcquirer.getIssuers()) {
|
||||
issLoadContext.setId(_oldIssuer.getId());
|
||||
issLoadContext.setView("issuer-view");
|
||||
Issuer oldIssuer = dataManager.load(issLoadContext);
|
||||
List<Card> theCards = new ArrayList<>();
|
||||
for(Card _card : oldIssuer.getCards()) {
|
||||
loadContext.setView("card-view");
|
||||
loadContext.setId(_card.getId());
|
||||
Card card = dataManager.load(loadContext);
|
||||
theCards.add(card);
|
||||
}
|
||||
issuerCards.put(oldIssuer.getId(), theCards);
|
||||
}
|
||||
}
|
||||
|
||||
// terminal link
|
||||
TerminalLink link = metadata.create(TerminalLink.class);
|
||||
// terminal
|
||||
Terminal terminal = metadata.create(Terminal.class);
|
||||
terminal.setSn(sn);
|
||||
terminal.setImei(imei);
|
||||
terminal.setGroup(groups);
|
||||
terminal.setModel(model);
|
||||
terminal.setProfile(profile);
|
||||
terminal.setMerchant(merchant);
|
||||
|
||||
TerminalExt terminalExt = metadataTools.deepCopy(templateTerminal);
|
||||
terminalExt.setId(uuidSource.createUuid());
|
||||
terminalExt.setTerminalId(terminalId);
|
||||
terminalExt.setMerchantId(merchantId);
|
||||
terminalExt.setMerchantName1(merchantName1);
|
||||
terminalExt.setMerchantName2(merchantName2);
|
||||
terminalExt.setMerchantName3(merchantName3);
|
||||
|
||||
link.setTerminal(terminal);
|
||||
link.setTerminalExt(terminalExt);
|
||||
|
||||
terminalExt.getAcquirers().clear();
|
||||
// acquirers
|
||||
List<Acquirer> acquirers = new ArrayList<>();
|
||||
templateAcquirers.forEach(_acquirer -> {
|
||||
Acquirer acquirer = metadataTools.deepCopy(_acquirer);
|
||||
acquirer.setId(uuidSource.createUuid());
|
||||
acquirer.setAcquirerType(_acquirer.getAcquirerType());
|
||||
acquirer.setTerminalID(terminalId);
|
||||
acquirer.setMerchantID(merchantId);
|
||||
acquirer.setTleSetting(_acquirer.getTleSetting());
|
||||
acquirer.getIssuers().clear();
|
||||
acquirers.add(acquirer);
|
||||
|
||||
// issuers
|
||||
List<Issuer> templateIssuers = _acquirer.getIssuers();
|
||||
List<Issuer> issuers = new ArrayList<>();
|
||||
templateIssuers.forEach(_issuer -> {
|
||||
Issuer issuer = metadataTools.deepCopy(_issuer);
|
||||
issuer.setId(uuidSource.createUuid());
|
||||
issuer.setOnUs(_issuer.getOnUs());
|
||||
issuers.add(issuer);
|
||||
issuer.getCards().clear();
|
||||
|
||||
issuerIds.put(issuer.getId(), _issuer.getId());
|
||||
});
|
||||
acquirer.setIssuers(issuers);
|
||||
});
|
||||
terminalExt.setAcquirers(acquirers);
|
||||
|
||||
// add to terminal links
|
||||
terminalLinks.add(link);
|
||||
}
|
||||
} catch (ImportFileEofEvaluationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
private void _parseTerminalsFromEditFile(Workbook workbook, Sheet sheet, List<TerminalExt> terminals) throws Exception {
|
||||
{
|
||||
metadata = AppBeans.get(Metadata.NAME);
|
||||
terminalEditAttributesToColumns = createEditTerminalAttributesToColumns();
|
||||
|
||||
// read the values
|
||||
firstDataRowIndex = 2;
|
||||
|
||||
try {
|
||||
for (currentRowIndex = firstDataRowIndex; !eofEditTerminal(sheet.getRow(currentRowIndex)); currentRowIndex += dataRowIncrement) {
|
||||
Row row = sheet.getRow(currentRowIndex);
|
||||
|
||||
String terminalId = (String) XlsHelper.getCellValue(row.getCell(terminalEditAttributesToColumns.get(TERMINAL_ID)));
|
||||
String merchantName1 = (String) (XlsHelper.getCellValue(row.getCell(terminalEditAttributesToColumns.get(MERCHANT_NAME1))));
|
||||
String merchantName2 = (String) (XlsHelper.getCellValue(row.getCell(terminalEditAttributesToColumns.get(MERCHANT_NAME2))));
|
||||
String merchantName3 = (String) (XlsHelper.getCellValue(row.getCell(terminalEditAttributesToColumns.get(MERCHANT_NAME3))));
|
||||
|
||||
// load existing terminal
|
||||
log.info("Terminal ID: " + terminalId);
|
||||
TerminalExt terminalExt = loadMinimumTerminalExtByTerminalId(terminalId);
|
||||
log.info("Terminal Ext: " + terminalExt);
|
||||
terminalExt.setMerchantName1(merchantName1);
|
||||
terminalExt.setMerchantName2(merchantName2);
|
||||
terminalExt.setMerchantName3(merchantName3);
|
||||
// add to terminals
|
||||
terminals.add(terminalExt);
|
||||
}
|
||||
} catch (ImportFileEofEvaluationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveTerminals(List<TerminalLink> terminalLinks) throws Exception {
|
||||
log.info("Saving terminal links ...");
|
||||
/*
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
// prepare commit context
|
||||
CommitContext commitContext = new CommitContext();
|
||||
|
||||
terminalLinks.forEach(terminalLink -> {
|
||||
// Terminal
|
||||
Terminal terminal = terminalLink.getTerminal();
|
||||
// Terminal Ext
|
||||
TerminalExt terminalExt = terminalLink.getTerminalExt();
|
||||
|
||||
// Order of saving
|
||||
// 1. Terminal
|
||||
// 2. Terminal Ext
|
||||
// 3. Acquirers
|
||||
// 4. Issuers + Link Card
|
||||
commitContext.addInstanceToCommit(terminal);
|
||||
commitContext.addInstanceToCommit(terminalExt);
|
||||
terminalExt.getAcquirers().forEach(acquirer -> {
|
||||
log.info(">>>> Acquirer#id: " + acquirer.getId() + " (" + acquirer.getName() + ")");
|
||||
acquirer.setTerminalExt(terminalExt);
|
||||
commitContext.addInstanceToCommit(acquirer);
|
||||
|
||||
acquirer.getIssuers().forEach(issuer -> {
|
||||
issuer.setAcquirer(acquirer);
|
||||
log.info(">>>> Issuer#id: " + issuer.getId() + " (" + issuer.getName() + ")");
|
||||
commitContext.addInstanceToCommit(issuer);
|
||||
});
|
||||
});
|
||||
commitContext.addInstanceToCommit(terminalLink);
|
||||
dataManager.commit(commitContext);
|
||||
commitContext.getCommitInstances().clear();
|
||||
|
||||
// last one save cards
|
||||
LoadContext<Issuer> issLoadContext = LoadContext.create(Issuer.class);
|
||||
terminalExt.getAcquirers().forEach(acquirer -> {
|
||||
acquirer.getIssuers().forEach(_issuer -> {
|
||||
issLoadContext.setId(_issuer.getId());
|
||||
issLoadContext.setView("issuer-view");
|
||||
Issuer issuer = dataManager.load(issLoadContext);
|
||||
UUID oldIssuerId = issuerIds.get(_issuer.getId());
|
||||
List<Card> oldCards = issuerCards.get(oldIssuerId);
|
||||
List<Card> cards = new ArrayList<>();
|
||||
for(Card _card : oldCards) {
|
||||
cards.add(_card);
|
||||
}
|
||||
issuer.setCards(cards);
|
||||
System.out.println("TOTAL CARDS: " + issuer.getCards().size());
|
||||
//commitContext.addInstanceToCommit(issuer);
|
||||
dataManager.commit(issuer);
|
||||
});
|
||||
});
|
||||
//dataManager.commit(commitContext);
|
||||
});
|
||||
|
||||
tx.commit();
|
||||
} finally {
|
||||
log.info("Saving terminal links DONE");
|
||||
}*/
|
||||
}
|
||||
|
||||
private Boolean eofByColumnNullValueTerminal(Row row, String columnAlias) throws ImportFileEofEvaluationException {
|
||||
Integer columnNumber = terminalAttributesToColumns.get(columnAlias);
|
||||
if (row != null) {
|
||||
Cell cell = row.getCell(columnNumber);
|
||||
Object cellValue;
|
||||
try {
|
||||
cellValue = XlsHelper.getCellValue(cell);
|
||||
} catch (Exception e) {
|
||||
throw new ImportFileEofEvaluationException(String.format("Eof evaluation has failed on row %s", row.getRowNum()));
|
||||
}
|
||||
return cellValue == null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Boolean eofByColumnNullValueEditTerminal(Row row, String columnAlias) throws ImportFileEofEvaluationException {
|
||||
Integer columnNumber = terminalEditAttributesToColumns.get(columnAlias);
|
||||
if (row != null) {
|
||||
Cell cell = row.getCell(columnNumber);
|
||||
Object cellValue;
|
||||
try {
|
||||
cellValue = XlsHelper.getCellValue(cell);
|
||||
} catch (Exception e) {
|
||||
throw new ImportFileEofEvaluationException(String.format("Eof evaluation has failed on row %s", row.getRowNum()));
|
||||
}
|
||||
return cellValue == null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Map<String, Integer> createTerminalAttributesToColumns() {
|
||||
Map<String, Integer> columns = new HashMap<>();
|
||||
|
||||
columns.put(SN, 0);
|
||||
columns.put(IMEI, 1);
|
||||
columns.put(MODEL_NAME, 2);
|
||||
columns.put(MERCHANT_NAME, 3);
|
||||
columns.put(PROFILE_NAME, 4);
|
||||
columns.put(GROUP_NAMES, 5);
|
||||
columns.put(TEMPLATE_TERMINAL_ID, 6);
|
||||
columns.put(TERMINAL_ID, 7);
|
||||
columns.put(MERCHANT_ID, 8);
|
||||
columns.put(MERCHANT_NAME1, 9);
|
||||
columns.put(MERCHANT_NAME2, 10);
|
||||
columns.put(MERCHANT_NAME3, 11);
|
||||
columns.put(MERCHANT_NAME4, 12);
|
||||
columns.put(MERCHANT_NAME5, 13);
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
private Map<String, Integer> createEditTerminalAttributesToColumns() {
|
||||
Map<String, Integer> columns = new HashMap<>();
|
||||
|
||||
columns.put(TERMINAL_ID, 0);
|
||||
columns.put(MERCHANT_NAME1, 1);
|
||||
columns.put(MERCHANT_NAME2, 2);
|
||||
columns.put(MERCHANT_NAME3, 3);
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
protected boolean eofTerminal(Row row) throws ImportFileEofEvaluationException {
|
||||
return eofByColumnNullValueTerminal(row, SN);
|
||||
}
|
||||
|
||||
protected boolean eofEditTerminal(Row row) throws ImportFileEofEvaluationException {
|
||||
return eofByColumnNullValueEditTerminal(row, SN);
|
||||
}
|
||||
|
||||
protected boolean eofProfile(Row row) throws ImportFileEofEvaluationException {
|
||||
int maxRow = SpreadsheetVersion.EXCEL97.getLastRowIndex();
|
||||
//return eofByColumnNullValueProfile(row, PROFILE_CODE);
|
||||
return row == null || row.getRowNum() > maxRow;
|
||||
}
|
||||
|
||||
private DeviceProfile loadDeviceProfileByName(String profileName) {
|
||||
LoadContext<DeviceProfile> loadContext = LoadContext.create(DeviceProfile.class);
|
||||
loadContext.setQueryString("select m from tms_DeviceProfile m where lower(m.name)=:name and m.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("name", profileName.toLowerCase());
|
||||
loadContext.setView("deviceProfile-minimal-view");
|
||||
DeviceProfile profile = dataManager.load(loadContext);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private DeviceModel loadDeviceModelByName(String modelName) {
|
||||
LoadContext<DeviceModel> loadContext = LoadContext.create(DeviceModel.class);
|
||||
loadContext.setQueryString("select m from tms_DeviceModel m where lower(m.model)=:name and m.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("name", modelName.toLowerCase());
|
||||
loadContext.setView("deviceModel-minimal-view");
|
||||
DeviceModel model = dataManager.load(loadContext);
|
||||
return model;
|
||||
}
|
||||
|
||||
private Merchant loadMerchantByName(String merchantName) {
|
||||
LoadContext<Merchant> loadContext = LoadContext.create(Merchant.class);
|
||||
loadContext.setQueryString("select m from tms_Merchant m where lower(m.name)=:name and m.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("name", merchantName.toLowerCase());
|
||||
loadContext.setView("merchant-minimal-view");
|
||||
Merchant merchant = dataManager.load(loadContext);
|
||||
return merchant;
|
||||
}
|
||||
|
||||
private TerminalGroup loadTerminalGroupByName(String groupName) {
|
||||
LoadContext<TerminalGroup> loadContext = LoadContext.create(TerminalGroup.class);
|
||||
loadContext.setQueryString("select m from tms_TerminalGroup m where lower(m.name)=:name and m.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("name", groupName.toLowerCase());
|
||||
loadContext.setView("terminalGroup-view");
|
||||
TerminalGroup terminalGroup = dataManager.load(loadContext);
|
||||
return terminalGroup;
|
||||
}
|
||||
|
||||
private TerminalExt loadTerminalExtByTerminalId(String terminalID) {
|
||||
LoadContext<TerminalExt> loadContext = LoadContext.create(TerminalExt.class);
|
||||
loadContext.setQueryString("select m from tms$TerminalExt m where m.terminalId=:terminalId and m.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("terminalId", terminalID);
|
||||
loadContext.setView("terminalExt-full-view");
|
||||
TerminalExt terminal = dataManager.load(loadContext);
|
||||
return terminal;
|
||||
}
|
||||
|
||||
private TerminalExt loadMinimumTerminalExtByTerminalId(String terminalID) {
|
||||
LoadContext<TerminalExt> loadContext = LoadContext.create(TerminalExt.class);
|
||||
loadContext.setQueryString("select m from tms$TerminalExt m where m.terminalId=:terminalId and m.deleteTs is null");
|
||||
loadContext.getQuery().setParameter("terminalId", terminalID);
|
||||
loadContext.setView("terminalExt-minimal2-view");
|
||||
TerminalExt terminal = dataManager.load(loadContext);
|
||||
return terminal;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package com.cmobile.unifiedtms.service;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.TerminalLink;
|
||||
import com.cmobile.unifiedtms.entity.restmodel.result.data.Feature;
|
||||
import com.cmobile.unifiedtms.entity.restmodel.result.data.TerminalFeatures;
|
||||
import com.cmobile.unifiedtms.ext.entity.TerminalExt;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.core.global.Messages;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Service(TerminalService.NAME)
|
||||
public class TerminalServiceBean implements TerminalService {
|
||||
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
@Inject
|
||||
private Messages messages;
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private Metadata metadata;
|
||||
|
||||
@Override
|
||||
public TerminalFeatures getTerminalByTid(String tid) {
|
||||
LoadContext<TerminalLink> loadContextTerminalLink = LoadContext.create(TerminalLink.class);
|
||||
loadContextTerminalLink.setQueryString("select t from tms_TerminalLink t where t.terminalExt.terminalId=:terminalId");
|
||||
loadContextTerminalLink.getQuery().setParameter("terminalId", tid);
|
||||
loadContextTerminalLink.setView("terminalLink-local-view");
|
||||
TerminalLink terminalLink = dataManager.load(loadContextTerminalLink);
|
||||
|
||||
if(terminalLink != null) {
|
||||
Terminal terminal = terminalLink.getTerminal();
|
||||
TerminalExt terminalExt = terminalLink.getTerminalExt();
|
||||
|
||||
TerminalFeatures terminalFeatures = new TerminalFeatures();
|
||||
terminalFeatures.setSn(terminal.getSn());
|
||||
terminalFeatures.setImei(terminal.getImei());
|
||||
terminalFeatures.setTerminalId(terminalExt.getTerminalId());
|
||||
terminalFeatures.setProvider(terminal.getCellName());
|
||||
terminalFeatures.setAppVersion(terminal.getVfssVersion());
|
||||
terminalFeatures.setLatitude(terminal.getLatitude());
|
||||
terminalFeatures.setLongitude(terminal.getLongitude());
|
||||
List<Feature> features = new ArrayList<>();
|
||||
|
||||
List<Field> allFields = Arrays.asList(TerminalExt.class.getDeclaredFields());
|
||||
allFields.forEach(field -> {
|
||||
field.setAccessible(true);
|
||||
String fieldName = field.getName();
|
||||
if(fieldName.startsWith("feature")) {
|
||||
try {
|
||||
String label = messages.getMessage("com.cmobile.unifiedtms.ext.entity", "TerminalExt." + fieldName);
|
||||
Boolean value = (Boolean) field.get(terminalExt);
|
||||
features.add(new Feature(label, booleanValue(value)));
|
||||
} catch(IllegalAccessException iaex) {
|
||||
logger.error("Error getting field value: {}", fieldName, iaex);
|
||||
}
|
||||
}
|
||||
});
|
||||
terminalFeatures.setFeatureTransactions(features);
|
||||
return terminalFeatures;
|
||||
} else {
|
||||
// tid not found or not linked yet
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean booleanValue(Boolean val) {
|
||||
return val != null ? val.booleanValue() : false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.cmobile.unifiedtms.service.controllers;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.Terminal;
|
||||
import com.cmobile.unifiedtms.entity.TerminalLink;
|
||||
import com.cmobile.unifiedtms.entity.restmodel.result.data.Feature;
|
||||
import com.cmobile.unifiedtms.entity.restmodel.result.data.TerminalFeatures;
|
||||
import com.cmobile.unifiedtms.ext.entity.TerminalExt;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.core.global.Messages;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class TerminalController {
|
||||
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
@Inject
|
||||
private Messages messages;
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Inject
|
||||
private Metadata metadata;
|
||||
|
||||
@GetMapping("/terminal/{tid}")
|
||||
public ResponseEntity<Object> getTerminal(
|
||||
@PathVariable("tid") String tid
|
||||
) {
|
||||
|
||||
LoadContext<TerminalLink> loadContextTerminalLink = LoadContext.create(TerminalLink.class);
|
||||
loadContextTerminalLink.setQueryString("select t from tms_TerminalLink t where t.terminalExt.terminalId=:terminalId");
|
||||
loadContextTerminalLink.getQuery().setParameter("terminalId", tid);
|
||||
loadContextTerminalLink.setView("terminalLink-local-view");
|
||||
TerminalLink terminalLink = dataManager.load(loadContextTerminalLink);
|
||||
|
||||
if(terminalLink != null) {
|
||||
Terminal terminal = terminalLink.getTerminal();
|
||||
TerminalExt terminalExt = terminalLink.getTerminalExt();
|
||||
|
||||
TerminalFeatures terminalFeatures = new TerminalFeatures();
|
||||
terminalFeatures.setSn(terminal.getSn());
|
||||
terminalFeatures.setImei(terminal.getImei());
|
||||
terminalFeatures.setTerminalId(terminalExt.getTerminalId());
|
||||
terminalFeatures.setProvider(terminal.getCellName());
|
||||
terminalFeatures.setAppVersion(terminal.getVfssVersion());
|
||||
List<Feature> features = new ArrayList<>();
|
||||
|
||||
List<Field> allFields = Arrays.asList(TerminalExt.class.getDeclaredFields());
|
||||
allFields.forEach(field -> {
|
||||
String fieldName = field.getName();
|
||||
if(fieldName.startsWith("feature")) {
|
||||
try {
|
||||
Boolean value = (Boolean) field.get(terminalExt);
|
||||
features.add(new Feature(fieldName, booleanValue(value)));
|
||||
} catch(IllegalAccessException iaex) {
|
||||
logger.error("Error getting field value: {}", fieldName, iaex);
|
||||
}
|
||||
}
|
||||
});
|
||||
terminalFeatures.setFeatureTransactions(features);
|
||||
return ResponseEntity.ok(terminalFeatures);
|
||||
} else {
|
||||
// tid not found or not linked yet
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean booleanValue(Boolean val) {
|
||||
return val != null ? val.booleanValue() : false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
rest-messages.success=Success
|
||||
rest-messages.failure=Failure
|
||||
rest-messages.not-found=Data Not Found
|
||||
rest-messages.already-exists=Already Exists
|
||||
rest-messages.not-contained=Not Contained
|
||||
rest-messages.invalid-file=Invalid File
|
||||
rest-messages.invalid-params=Invalid Params
|
||||
rest-messages.not-linked=Not Linked
|
||||
rest-messages.general-error=System Exception Error
|
||||
@ -0,0 +1,9 @@
|
||||
rest-messages.success=Sukses
|
||||
rest-messages.failure=Gagal
|
||||
rest-messages.not-found=Data Tidak Ditemukan
|
||||
rest-messages.already-exists=Sudah Ada
|
||||
rest-messages.not-contained=Tidak Termasuk
|
||||
rest-messages.invalid-file=File Tidak Valid
|
||||
rest-messages.invalid-params=Parameter Tidak Valid
|
||||
rest-messages.not-linked=Tidak Disambungkan
|
||||
rest-messages.general-error=Error Sistem
|
||||
8
modules/core/src/com/cmobile/unifiedtms/spring.xml
Normal file
8
modules/core/src/com/cmobile/unifiedtms/spring.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:context="http://www.springframework.org/schema/context">
|
||||
|
||||
<!-- Annotation-based beans -->
|
||||
<context:component-scan base-package="com.cmobile.unifiedtms"/>
|
||||
|
||||
</beans>
|
||||
32
modules/core/src/com/cmobile/unifiedtms/utils/ISOUtil.java
Normal file
32
modules/core/src/com/cmobile/unifiedtms/utils/ISOUtil.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.cmobile.unifiedtms.utils;
|
||||
|
||||
public final class ISOUtil {
|
||||
|
||||
public static byte[] hex2byte(byte[] b, int offset, int len) {
|
||||
byte[] d = new byte[len];
|
||||
for (int i = 0; i < len * 2; i++) {
|
||||
int shift = (i % 2 == 1) ? 0 : 4;
|
||||
d[i >> 1] = (byte)(d[i >> 1] | Character.digit((char)b[offset + i], 16) << shift);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public static byte[] hex2byte(String s) {
|
||||
if (s.length() % 2 == 0) {
|
||||
return hex2byte(s.getBytes(), 0, s.length() >> 1);
|
||||
}
|
||||
throw new RuntimeException("Uneven number(" + s.length() + ") of hex digits passed to hex2byte.");
|
||||
}
|
||||
|
||||
public static String hexString(byte[] b) {
|
||||
StringBuilder d = new StringBuilder(b.length * 2);
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
char hi = Character.forDigit(b[i] >> 4 & 0xF, 16);
|
||||
char lo = Character.forDigit(b[i] & 0xF, 16);
|
||||
d.append(Character.toUpperCase(hi));
|
||||
d.append(Character.toUpperCase(lo));
|
||||
}
|
||||
return d.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package com.cmobile.unifiedtms.utils;
|
||||
|
||||
import com.cmobile.unifiedtms.entity.misc.TerminalUpdateCacheObj;
|
||||
import com.drew.metadata.mov.atoms.Atom;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class TerminalUpdateCache {
|
||||
|
||||
private Map<String, TerminalUpdateCacheObj> caches = new LinkedHashMap<>();
|
||||
private long considerDonePeriod = (10 * 60 * 1000); // 10 minutes
|
||||
private long releaseFromCachePeriod = 3 * 24 * 60 * 60 * 1000; // 3 days
|
||||
private int partialCapacity = 1000;
|
||||
private AtomicInteger currentCapacity = new AtomicInteger(0); // started & ongoing status
|
||||
|
||||
public TerminalUpdateCache() {}
|
||||
|
||||
public void setCapacity(int capacity) {
|
||||
this.partialCapacity = capacity;
|
||||
}
|
||||
|
||||
public int put(String key, TerminalUpdateCacheObj obj) {
|
||||
this.caches.put(key, obj);
|
||||
return this.currentCapacity.incrementAndGet();
|
||||
}
|
||||
|
||||
// update to finished status
|
||||
public int updateToFinished(String key, TerminalUpdateCacheObj obj) {
|
||||
this.caches.put(key, obj);
|
||||
return this.currentCapacity.decrementAndGet();
|
||||
}
|
||||
|
||||
public TerminalUpdateCacheObj get(String key) {
|
||||
return this.caches.get(key);
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
return this.currentCapacity.get() >= this.partialCapacity;//this.currentCapacity.get() >= this.partialCapacity;
|
||||
}
|
||||
|
||||
public void runUpdateStatus() {
|
||||
Set<Map.Entry<String, TerminalUpdateCacheObj>> entrySet = this.caches.entrySet();
|
||||
entrySet.forEach(entry -> {
|
||||
TerminalUpdateCacheObj obj = entry.getValue();
|
||||
Date createdTime = obj.getCreatedTime();
|
||||
long now = System.currentTimeMillis();
|
||||
long elapsed = createdTime.getTime() - now;
|
||||
// if still ongoing status
|
||||
if("Ongoing".equalsIgnoreCase(obj.getStatus())) {
|
||||
if(elapsed >= considerDonePeriod) {
|
||||
obj.considerDone();
|
||||
this.updateToFinished(entry.getKey(), obj);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cleanupCache() {
|
||||
Set<Map.Entry<String, TerminalUpdateCacheObj>> entrySet = this.caches.entrySet();
|
||||
entrySet.forEach(entry -> {
|
||||
TerminalUpdateCacheObj obj = entry.getValue();
|
||||
Date createdTime = obj.getCreatedTime();
|
||||
long now = System.currentTimeMillis();
|
||||
long elapsed = createdTime.getTime() - now;
|
||||
// if still ongoing status
|
||||
if("Finished".equalsIgnoreCase(obj.getStatus())) {
|
||||
if(elapsed >= releaseFromCachePeriod) {
|
||||
this.caches.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Map<String, TerminalUpdateCacheObj> getCaches() {
|
||||
return caches;
|
||||
}
|
||||
|
||||
public AtomicInteger getCurrentCapacity() {
|
||||
return currentCapacity;
|
||||
}
|
||||
}
|
||||
175
modules/core/src/com/cmobile/unifiedtms/utils/XlsHelper.java
Normal file
175
modules/core/src/com/cmobile/unifiedtms/utils/XlsHelper.java
Normal file
@ -0,0 +1,175 @@
|
||||
package com.cmobile.unifiedtms.utils;
|
||||
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.Resources;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellType;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by aleksey on 18/10/2016.
|
||||
*
|
||||
* Class helping to work with .xls and .xlsx files
|
||||
*
|
||||
*/
|
||||
public class XlsHelper {
|
||||
protected static final char NON_BREAKING_SPACE = (char) 160;
|
||||
protected static Resources resources = AppBeans.get(Resources.NAME);
|
||||
|
||||
/**
|
||||
* Method returns workbook for .xls and .xlsx files
|
||||
*/
|
||||
@Nullable
|
||||
public static Workbook openWorkbook(@Nullable String path) throws IOException {
|
||||
Workbook workbook = null;
|
||||
InputStream is = resources.getResourceAsStream(path);
|
||||
String extension = FilenameUtils.getExtension(path);
|
||||
if (is != null) {
|
||||
if ("xls".equals(extension)) {
|
||||
workbook = new HSSFWorkbook(is);
|
||||
} else if ("xlsx".equals(extension)) {
|
||||
workbook = new XSSFWorkbook(is);
|
||||
}
|
||||
is.close();
|
||||
}
|
||||
return workbook;
|
||||
}
|
||||
|
||||
public static Workbook openWorkbook(byte[] bytes) throws IOException {
|
||||
Workbook workbook;
|
||||
try {
|
||||
InputStream is = new ByteArrayInputStream(bytes);
|
||||
workbook = new HSSFWorkbook(new POIFSFileSystem(is));
|
||||
is.close();
|
||||
} catch (OfficeXmlFileException exc) {
|
||||
InputStream is = new ByteArrayInputStream(bytes);
|
||||
workbook = new XSSFWorkbook(is);
|
||||
is.close();
|
||||
}
|
||||
return workbook;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object getCellValue(Cell cell) throws Exception {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case BLANK:
|
||||
return null;
|
||||
case STRING:
|
||||
String formattedCellValue = cell.getStringCellValue().replace(String.valueOf(NON_BREAKING_SPACE), " ").trim();
|
||||
return formattedCellValue.isEmpty() ? null : formattedCellValue;
|
||||
case NUMERIC:
|
||||
if (isDateCell(cell)) {
|
||||
return cell.getDateCellValue();
|
||||
}
|
||||
|
||||
Double numericCellValue = cell.getNumericCellValue();
|
||||
if (!isAlmostInt(numericCellValue))
|
||||
return numericCellValue;
|
||||
else {
|
||||
if (numericCellValue > Integer.MAX_VALUE) {
|
||||
Long value = numericCellValue.longValue();
|
||||
return value;
|
||||
} else {
|
||||
Integer value = numericCellValue.intValue();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
case FORMULA:
|
||||
return getFormulaCellValue(cell);
|
||||
default:
|
||||
throw new IllegalStateException(String.format("Cell type '%s' is not supported", cell.getCellType()));
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean isDateCell(Cell cell) {
|
||||
return HSSFDateUtil.isCellDateFormatted(cell);
|
||||
}
|
||||
|
||||
protected static boolean isAlmostInt(Double numericCellValue) {
|
||||
return Math.abs(numericCellValue - numericCellValue.longValue()) < 1e-10;
|
||||
}
|
||||
|
||||
protected static Object getFormulaCellValue(Cell cell) {
|
||||
String formattedCellValue;
|
||||
try {
|
||||
formattedCellValue = cell.getStringCellValue().replace(String.valueOf(NON_BREAKING_SPACE), " ").trim();
|
||||
} catch (IllegalStateException e) {
|
||||
return "Formula error";
|
||||
}
|
||||
switch (cell.getCachedFormulaResultType()) {
|
||||
case NUMERIC:
|
||||
return cell.getNumericCellValue();
|
||||
case STRING:
|
||||
if (formattedCellValue.isEmpty())
|
||||
return null;
|
||||
return formattedCellValue;
|
||||
default:
|
||||
throw new IllegalStateException(String.format("Formula cell type '%s' is not supported", cell.getCachedFormulaResultType()));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object getCellValue(Cell cell, Boolean forceToString) throws Exception {
|
||||
if (cell == null || cell.getCellType() == CellType.BLANK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
cell.setCellType(CellType.STRING);
|
||||
return cell.getStringCellValue().replace(String.valueOf(NON_BREAKING_SPACE), " ").trim();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T extends Object> T getParameterValue(Map<String, Object> values, String parameter) {
|
||||
if (values.get(parameter) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (T) values.get(parameter);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Double getParameterDoubleValue(Map<String, Object> values, String parameter) {
|
||||
if (values.get(parameter) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Double.valueOf(values.get(parameter).toString());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Integer getParameterIntegerValue(Map<String, Object> values, String parameter) {
|
||||
Object value = values.get(parameter);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Number)
|
||||
return ((Number) value).intValue();
|
||||
return Integer.valueOf(value.toString());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getParameterStringValue(Map<String, Object> values, String parameter) {
|
||||
if (values.get(parameter) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return values.get(parameter).toString().trim();
|
||||
}
|
||||
|
||||
}
|
||||
240
modules/core/src/sasc/emv/EMVTags.java
Normal file
240
modules/core/src/sasc/emv/EMVTags.java
Normal file
@ -0,0 +1,240 @@
|
||||
package sasc.emv;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import sasc.iso7816.Tag;
|
||||
import sasc.iso7816.TagImpl;
|
||||
import sasc.iso7816.TagValueType;
|
||||
import sasc.util.ByteArrayWrapper;
|
||||
import sasc.util.Util;
|
||||
|
||||
public class EMVTags {
|
||||
|
||||
private static LinkedHashMap<IssuerIdentificationNumber, LinkedHashMap<ByteArrayWrapper, Tag>> issuerToTagsMap = new LinkedHashMap();
|
||||
private static LinkedHashMap<ByteArrayWrapper, LinkedHashMap<ByteArrayWrapper, Tag>> paymentSystemToTagsMap = new LinkedHashMap();
|
||||
|
||||
private static LinkedHashMap<ByteArrayWrapper, Tag> tags = new LinkedHashMap();
|
||||
|
||||
public static final Tag UNIVERSAL_TAG_FOR_OID = new TagImpl("06", TagValueType.BINARY, "Object Identifier (OID)", "Universal tag for OID");
|
||||
public static final Tag COUNTRY_CODE = new TagImpl("41", TagValueType.NUMERIC, "Country Code", "Country code (encoding specified in ISO 3166-1) and optional national data");
|
||||
public static final Tag ISSUER_IDENTIFICATION_NUMBER = new TagImpl("42", TagValueType.NUMERIC, "Issuer Identification Number (IIN)", "The number that identifies the major industry and the card issuer and that forms the first part of the Primary Account Number (PAN)");
|
||||
|
||||
public static final Tag AID_CARD = new TagImpl("4f", TagValueType.BINARY, "Application Identifier (AID) - card", "Identifies the application as described in ISO/IEC 7816-5");
|
||||
public static final Tag APPLICATION_LABEL = new TagImpl("50", TagValueType.TEXT, "Application Label", "Mnemonic associated with the AID according to ISO/IEC 7816-5");
|
||||
public static final Tag PATH = new TagImpl("51", TagValueType.BINARY, "File reference data element", "ISO-7816 Path");
|
||||
public static final Tag COMMAND_APDU = new TagImpl("52", TagValueType.BINARY, "Command APDU", "");
|
||||
public static final Tag DISCRETIONARY_DATA_OR_TEMPLATE = new TagImpl("53", TagValueType.BINARY, "Discretionary data (or template)", "");
|
||||
public static final Tag APPLICATION_TEMPLATE = new TagImpl("61", TagValueType.BINARY, "Application Template", "Contains one or more data objects relevant to an application directory entry according to ISO/IEC 7816-5");
|
||||
public static final Tag FCI_TEMPLATE = new TagImpl("6f", TagValueType.BINARY, "File Control Information (FCI) Template", "Set of file control parameters and file management data (according to ISO/IEC 7816-4)");
|
||||
public static final Tag DD_TEMPLATE = new TagImpl("73", TagValueType.BINARY, "Directory Discretionary Template", "Issuer discretionary part of the directory according to ISO/IEC 7816-5");
|
||||
public static final Tag DEDICATED_FILE_NAME = new TagImpl("84", TagValueType.BINARY, "Dedicated File (DF) Name", "Identifies the name of the DF as described in ISO/IEC 7816-4");
|
||||
public static final Tag SFI = new TagImpl("88", TagValueType.BINARY, "Short File Identifier (SFI)", "Identifies the SFI to be used in the commands related to a given AEF or DDF. The SFI data object is a binary field with the three high order bits set to zero");
|
||||
|
||||
public static final Tag FCI_PROPRIETARY_TEMPLATE = new TagImpl("a5", TagValueType.BINARY, "File Control Information (FCI) Proprietary Template", "Identifies the data object proprietary to this specification in the FCI template according to ISO/IEC 7816-4");
|
||||
public static final Tag ISSUER_URL = new TagImpl("5f50", TagValueType.TEXT, "Issuer URL", "The URL provides the location of the Issuer’s Library Server on the Internet");
|
||||
|
||||
public static final Tag TRACK_2_EQV_DATA = new TagImpl("57", TagValueType.BINARY, "Track 2 Equivalent Data", "Contains the data elements of track 2 according to ISO/IEC 7813, excluding start sentinel, end sentinel, and Longitudinal Redundancy Check (LRC)");
|
||||
public static final Tag PAN = new TagImpl("5a", TagValueType.NUMERIC, "Application Primary Account Number (PAN)", "Valid cardholder account number");
|
||||
public static final Tag RECORD_TEMPLATE = new TagImpl("70", TagValueType.BINARY, "Record Template (EMV Proprietary)", "Template proprietary to the EMV specification");
|
||||
public static final Tag ISSUER_SCRIPT_TEMPLATE_1 = new TagImpl("71", TagValueType.BINARY, "Issuer Script Template 1", "Contains proprietary issuer data for transmission to the ICC before the second GENERATE AC command");
|
||||
public static final Tag ISSUER_SCRIPT_TEMPLATE_2 = new TagImpl("72", TagValueType.BINARY, "Issuer Script Template 2", "Contains proprietary issuer data for transmission to the ICC after the second GENERATE AC command");
|
||||
public static final Tag RESPONSE_MESSAGE_TEMPLATE_2 = new TagImpl("77", TagValueType.BINARY, "Response Message Template Format 2", "Contains the data objects (with tags and lengths) returned by the ICC in response to a command");
|
||||
public static final Tag RESPONSE_MESSAGE_TEMPLATE_1 = new TagImpl("80", TagValueType.BINARY, "Response Message Template Format 1", "Contains the data objects (without tags and lengths) returned by the ICC in response to a command");
|
||||
public static final Tag AMOUNT_AUTHORISED_BINARY = new TagImpl("81", TagValueType.BINARY, "Amount, Authorised (Binary)", "Authorised amount of the transaction (excluding adjustments)");
|
||||
public static final Tag APPLICATION_INTERCHANGE_PROFILE = new TagImpl("82", TagValueType.BINARY, "Application Interchange Profile", "Indicates the capabilities of the card to support specific functions in the application");
|
||||
public static final Tag COMMAND_TEMPLATE = new TagImpl("83", TagValueType.BINARY, "Command Template", "Identifies the data field of a command message");
|
||||
public static final Tag ISSUER_SCRIPT_COMMAND = new TagImpl("86", TagValueType.BINARY, "Issuer Script Command", "Contains a command for transmission to the ICC");
|
||||
public static final Tag APPLICATION_PRIORITY_INDICATOR = new TagImpl("87", TagValueType.BINARY, "Application Priority Indicator", "Indicates the priority of a given application or group of applications in a directory");
|
||||
public static final Tag AUTHORISATION_CODE = new TagImpl("89", TagValueType.BINARY, "Authorisation Code", "Value generated by the authorisation authority for an approved transaction");
|
||||
public static final Tag AUTHORISATION_RESPONSE_CODE = new TagImpl("8a", TagValueType.TEXT, "Authorisation Response Code", "Code that defines the disposition of a message");
|
||||
public static final Tag CDOL1 = new TagImpl("8c", TagValueType.DOL, "Card Risk Management Data Object List 1 (CDOL1)", "List of data objects (tag and length) to be passed to the ICC in the first GENERATE AC command");
|
||||
public static final Tag CDOL2 = new TagImpl("8d", TagValueType.DOL, "Card Risk Management Data Object List 2 (CDOL2)", "List of data objects (tag and length) to be passed to the ICC in the second GENERATE AC command");
|
||||
public static final Tag CVM_LIST = new TagImpl("8e", TagValueType.BINARY, "Cardholder Verification Method (CVM) List", "Identifies a method of verification of the cardholder supported by the application");
|
||||
public static final Tag CA_PUBLIC_KEY_INDEX_CARD = new TagImpl("8f", TagValueType.BINARY, "Certification Authority Public Key Index - card", "Identifies the certification authority’s public key in conjunction with the RID");
|
||||
public static final Tag ISSUER_PUBLIC_KEY_CERT = new TagImpl("90", TagValueType.BINARY, "Issuer Public Key Certificate", "Issuer public key certified by a certification authority");
|
||||
public static final Tag ISSUER_AUTHENTICATION_DATA = new TagImpl("91", TagValueType.BINARY, "Issuer Authentication Data", "Data sent to the ICC for online issuer authentication");
|
||||
public static final Tag ISSUER_PUBLIC_KEY_REMAINDER = new TagImpl("92", TagValueType.BINARY, "Issuer Public Key Remainder", "Remaining digits of the Issuer Public Key Modulus");
|
||||
public static final Tag SIGNED_STATIC_APP_DATA = new TagImpl("93", TagValueType.BINARY, "Signed Static Application Data", "Digital signature on critical application parameters for SDA");
|
||||
public static final Tag APPLICATION_FILE_LOCATOR = new TagImpl("94", TagValueType.BINARY, "Application File Locator (AFL)", "Indicates the location (SFI, range of records) of the AEFs related to a given application");
|
||||
public static final Tag TERMINAL_VERIFICATION_RESULTS = new TagImpl("95", TagValueType.BINARY, "Terminal Verification Results (TVR)", "Status of the different functions as seen from the terminal");
|
||||
public static final Tag TDOL = new TagImpl("97", TagValueType.BINARY, "Transaction Certificate Data Object List (TDOL)", "List of data objects (tag and length) to be used by the terminal in generating the TC Hash Value");
|
||||
public static final Tag TC_HASH_VALUE = new TagImpl("98", TagValueType.BINARY, "Transaction Certificate (TC) Hash Value", "Result of a hash function specified in Book 2, Annex B3.1");
|
||||
public static final Tag TRANSACTION_PIN_DATA = new TagImpl("99", TagValueType.BINARY, "Transaction Personal Identification Number (PIN) Data", "Data entered by the cardholder for the purpose of the PIN verification");
|
||||
public static final Tag TRANSACTION_DATE = new TagImpl("9a", TagValueType.NUMERIC, "Transaction Date", "Local date that the transaction was authorised");
|
||||
public static final Tag TRANSACTION_STATUS_INFORMATION = new TagImpl("9b", TagValueType.BINARY, "Transaction Status Information", "Indicates the functions performed in a transaction");
|
||||
public static final Tag TRANSACTION_TYPE = new TagImpl("9c", TagValueType.NUMERIC, "Transaction Type", "Indicates the type of financial transaction, represented by the first two digits of ISO 8583:1987 Processing Code");
|
||||
public static final Tag DDF_NAME = new TagImpl("9d", TagValueType.BINARY, "Directory Definition File (DDF) Name", "Identifies the name of a DF associated with a directory");
|
||||
|
||||
public static final Tag CARDHOLDER_NAME = new TagImpl("5f20", TagValueType.TEXT, "Cardholder Name", "Indicates cardholder name according to ISO 7813");
|
||||
public static final Tag APP_EXPIRATION_DATE = new TagImpl("5f24", TagValueType.NUMERIC, "Application Expiration Date", "Date after which application expires");
|
||||
public static final Tag APP_EFFECTIVE_DATE = new TagImpl("5f25", TagValueType.NUMERIC, "Application Effective Date", "Date from which the application may be used");
|
||||
public static final Tag ISSUER_COUNTRY_CODE = new TagImpl("5f28", TagValueType.NUMERIC, "Issuer Country Code", "Indicates the country of the issuer according to ISO 3166");
|
||||
public static final Tag TRANSACTION_CURRENCY_CODE = new TagImpl("5f2a", TagValueType.TEXT, "Transaction Currency Code", "Indicates the currency code of the transaction according to ISO 4217");
|
||||
public static final Tag LANGUAGE_PREFERENCE = new TagImpl("5f2d", TagValueType.TEXT, "Language Preference", "1–4 languages stored in order of preference, each represented by 2 alphabetical characters according to ISO 639");
|
||||
public static final Tag SERVICE_CODE = new TagImpl("5f30", TagValueType.NUMERIC, "Service Code", "Service code as defined in ISO/IEC 7813 for track 1 and track 2");
|
||||
public static final Tag PAN_SEQUENCE_NUMBER = new TagImpl("5f34", TagValueType.NUMERIC, "Application Primary Account Number (PAN) Sequence Number", "Identifies and differentiates cards with the same PAN");
|
||||
public static final Tag TRANSACTION_CURRENCY_EXP = new TagImpl("5f36", TagValueType.NUMERIC, "Transaction Currency Exponent", "Indicates the implied position of the decimal point from the right of the transaction amount represented according to ISO 4217");
|
||||
public static final Tag IBAN = new TagImpl("5f53", TagValueType.BINARY, "International Bank Account Number (IBAN)", "Uniquely identifies the account of a customer at a financial institution as defined in ISO 13616");
|
||||
public static final Tag BANK_IDENTIFIER_CODE = new TagImpl("5f54", TagValueType.MIXED, "Bank Identifier Code (BIC)", "Uniquely identifies a bank as defined in ISO 9362");
|
||||
public static final Tag ISSUER_COUNTRY_CODE_ALPHA2 = new TagImpl("5f55", TagValueType.TEXT, "Issuer Country Code (alpha2 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 2 character alphabetic code)");
|
||||
public static final Tag ISSUER_COUNTRY_CODE_ALPHA3 = new TagImpl("5f56", TagValueType.TEXT, "Issuer Country Code (alpha3 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 3 character alphabetic code)");
|
||||
public static final Tag ACQUIRER_IDENTIFIER = new TagImpl("9f01", TagValueType.NUMERIC, "Acquirer Identifier", "Uniquely identifies the acquirer within each payment system");
|
||||
public static final Tag AMOUNT_AUTHORISED_NUMERIC = new TagImpl("9f02", TagValueType.NUMERIC, "Amount, Authorised (Numeric)", "Authorised amount of the transaction (excluding adjustments)");
|
||||
public static final Tag AMOUNT_OTHER_NUMERIC = new TagImpl("9f03", TagValueType.NUMERIC, "Amount, Other (Numeric)", "Secondary amount associated with the transaction representing a cashback amount");
|
||||
public static final Tag AMOUNT_OTHER_BINARY = new TagImpl("9f04", TagValueType.NUMERIC, "Amount, Other (Binary)", "Secondary amount associated with the transaction representing a cashback amount");
|
||||
public static final Tag APP_DISCRETIONARY_DATA = new TagImpl("9f05", TagValueType.BINARY, "Application Discretionary Data", "Issuer or payment system specified data relating to the application");
|
||||
public static final Tag AID_TERMINAL = new TagImpl("9f06", TagValueType.BINARY, "Application Identifier (AID) - terminal", "Identifies the application as described in ISO/IEC 7816-5");
|
||||
public static final Tag APP_USAGE_CONTROL = new TagImpl("9f07", TagValueType.BINARY, "Application Usage Control", "Indicates issuer’s specified restrictions on the geographic usage and services allowed for the application");
|
||||
public static final Tag APP_VERSION_NUMBER_CARD = new TagImpl("9f08", TagValueType.BINARY, "Application Version Number - card", "Version number assigned by the payment system for the application");
|
||||
public static final Tag APP_VERSION_NUMBER_TERMINAL = new TagImpl("9f09", TagValueType.BINARY, "Application Version Number - terminal", "Version number assigned by the payment system for the application");
|
||||
public static final Tag CARDHOLDER_NAME_EXTENDED = new TagImpl("9f0b", TagValueType.TEXT, "Cardholder Name Extended", "Indicates the whole cardholder name when greater than 26 characters using the same coding convention as in ISO 7813");
|
||||
public static final Tag ISSUER_ACTION_CODE_DEFAULT = new TagImpl("9f0d", TagValueType.BINARY, "Issuer Action Code - Default", "Specifies the issuer’s conditions that cause a transaction to be rejected if it might have been approved online, but the terminal is unable to process the transaction online");
|
||||
public static final Tag ISSUER_ACTION_CODE_DENIAL = new TagImpl("9f0e", TagValueType.BINARY, "Issuer Action Code - Denial", "Specifies the issuer’s conditions that cause the denial of a transaction without attempt to go online");
|
||||
public static final Tag ISSUER_ACTION_CODE_ONLINE = new TagImpl("9f0f", TagValueType.BINARY, "Issuer Action Code - Online", "Specifies the issuer’s conditions that cause a transaction to be transmitted online");
|
||||
public static final Tag ISSUER_APPLICATION_DATA = new TagImpl("9f10", TagValueType.BINARY, "Issuer Application Data", "Contains proprietary application data for transmission to the issuer in an online transaction");
|
||||
public static final Tag ISSUER_CODE_TABLE_INDEX = new TagImpl("9f11", TagValueType.NUMERIC, "Issuer Code Table Index", "Indicates the code table according to ISO/IEC 8859 for displaying the Application Preferred Name");
|
||||
public static final Tag APP_PREFERRED_NAME = new TagImpl("9f12", TagValueType.TEXT, "Application Preferred Name", "Preferred mnemonic associated with the AID");
|
||||
public static final Tag LAST_ONLINE_ATC_REGISTER = new TagImpl("9f13", TagValueType.BINARY, "Last Online Application Transaction Counter (ATC) Register", "ATC value of the last transaction that went online");
|
||||
public static final Tag LOWER_CONSEC_OFFLINE_LIMIT = new TagImpl("9f14", TagValueType.BINARY, "Lower Consecutive Offline Limit", "Issuer-specified preference for the maximum number of consecutive offline transactions for this ICC application allowed in a terminal with online capability");
|
||||
public static final Tag MERCHANT_CATEGORY_CODE = new TagImpl("9f15", TagValueType.NUMERIC, "Merchant Category Code", "Classifies the type of business being done by the merchant, represented according to ISO 8583:1993 for Card Acceptor Business Code");
|
||||
public static final Tag MERCHANT_IDENTIFIER = new TagImpl("9f16", TagValueType.TEXT, "Merchant Identifier", "When concatenated with the Acquirer Identifier, uniquely identifies a given merchant");
|
||||
public static final Tag PIN_TRY_COUNTER = new TagImpl("9f17", TagValueType.BINARY, "Personal Identification Number (PIN) Try Counter", "Number of PIN tries remaining");
|
||||
public static final Tag ISSUER_SCRIPT_IDENTIFIER = new TagImpl("9f18", TagValueType.BINARY, "Issuer Script Identifier", "Identification of the Issuer Script");
|
||||
public static final Tag TERMINAL_COUNTRY_CODE = new TagImpl("9f1a", TagValueType.TEXT, "Terminal Country Code", "Indicates the country of the terminal, represented according to ISO 3166");
|
||||
public static final Tag TERMINAL_FLOOR_LIMIT = new TagImpl("9f1b", TagValueType.BINARY, "Terminal Floor Limit", "Indicates the floor limit in the terminal in conjunction with the AID");
|
||||
public static final Tag TERMINAL_IDENTIFICATION = new TagImpl("9f1c", TagValueType.TEXT, "Terminal Identification", "Designates the unique location of a terminal at a merchant");
|
||||
public static final Tag TERMINAL_RISK_MANAGEMENT_DATA = new TagImpl("9f1d", TagValueType.BINARY, "Terminal Risk Management Data", "Application-specific value used by the card for risk management purposes");
|
||||
public static final Tag INTERFACE_DEVICE_SERIAL_NUMBER = new TagImpl("9f1e", TagValueType.TEXT, "Interface Device (IFD) Serial Number", "Unique and permanent serial number assigned to the IFD by the manufacturer");
|
||||
public static final Tag TRACK1_DISCRETIONARY_DATA = new TagImpl("9f1f", TagValueType.TEXT, "[Magnetic Stripe] Track 1 Discretionary Data", "Discretionary part of track 1 according to ISO/IEC 7813");
|
||||
public static final Tag TRACK2_DISCRETIONARY_DATA = new TagImpl("9f20", TagValueType.TEXT, "[Magnetic Stripe] Track 2 Discretionary Data", "Discretionary part of track 2 according to ISO/IEC 7813");
|
||||
public static final Tag TRANSACTION_TIME = new TagImpl("9f21", TagValueType.NUMERIC, "Transaction Time (HHMMSS)", "Local time that the transaction was authorised");
|
||||
public static final Tag CA_PUBLIC_KEY_INDEX_TERMINAL = new TagImpl("9f22", TagValueType.BINARY, "Certification Authority Public Key Index - Terminal", "Identifies the certification authority’s public key in conjunction with the RID");
|
||||
public static final Tag UPPER_CONSEC_OFFLINE_LIMIT = new TagImpl("9f23", TagValueType.BINARY, "Upper Consecutive Offline Limit", "Issuer-specified preference for the maximum number of consecutive offline transactions for this ICC application allowed in a terminal without online capability");
|
||||
public static final Tag APP_CRYPTOGRAM = new TagImpl("9f26", TagValueType.BINARY, "Application Cryptogram", "Cryptogram returned by the ICC in response of the GENERATE AC command");
|
||||
public static final Tag CRYPTOGRAM_INFORMATION_DATA = new TagImpl("9f27", TagValueType.BINARY, "Cryptogram Information Data", "Indicates the type of cryptogram and the actions to be performed by the terminal");
|
||||
public static final Tag ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_CERT = new TagImpl("9f2d", TagValueType.BINARY, "ICC PIN Encipherment Public Key Certificate", "ICC PIN Encipherment Public Key certified by the issuer");
|
||||
public static final Tag ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_EXP = new TagImpl("9f2e", TagValueType.BINARY, "ICC PIN Encipherment Public Key Exponent", "ICC PIN Encipherment Public Key Exponent used for PIN encipherment");
|
||||
public static final Tag ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_REM = new TagImpl("9f2f", TagValueType.BINARY, "ICC PIN Encipherment Public Key Remainder", "Remaining digits of the ICC PIN Encipherment Public Key Modulus");
|
||||
public static final Tag ISSUER_PUBLIC_KEY_EXP = new TagImpl("9f32", TagValueType.BINARY, "Issuer Public Key Exponent", "Issuer public key exponent used for the verification of the Signed Static Application Data and the ICC Public Key Certificate");
|
||||
public static final Tag TERMINAL_CAPABILITIES = new TagImpl("9f33", TagValueType.BINARY, "Terminal Capabilities", "Indicates the card data input, CVM, and security capabilities of the terminal");
|
||||
public static final Tag CVM_RESULTS = new TagImpl("9f34", TagValueType.BINARY, "Cardholder Verification (CVM) Results", "Indicates the results of the last CVM performed");
|
||||
public static final Tag TERMINAL_TYPE = new TagImpl("9f35", TagValueType.NUMERIC, "Terminal Type", "Indicates the environment of the terminal, its communications capability, and its operational control");
|
||||
public static final Tag APP_TRANSACTION_COUNTER = new TagImpl("9f36", TagValueType.BINARY, "Application Transaction Counter (ATC)", "Counter maintained by the application in the ICC (incrementing the ATC is managed by the ICC)");
|
||||
public static final Tag UNPREDICTABLE_NUMBER = new TagImpl("9f37", TagValueType.BINARY, "Unpredictable Number", "Value to provide variability and uniqueness to the generation of a cryptogram");
|
||||
public static final Tag PDOL = new TagImpl("9f38", TagValueType.DOL, "Processing Options Data Object List (PDOL)", "Contains a list of terminal resident data objects (tags and lengths) needed by the ICC in processing the GET PROCESSING OPTIONS command");
|
||||
public static final Tag POINT_OF_SERVICE_ENTRY_MODE = new TagImpl("9f39", TagValueType.NUMERIC, "Point-of-Service (POS) Entry Mode", "Indicates the method by which the PAN was entered, according to the first two digits of the ISO 8583:1987 POS Entry Mode");
|
||||
public static final Tag AMOUNT_REFERENCE_CURRENCY = new TagImpl("9f3a", TagValueType.BINARY, "Amount, Reference Currency", "Authorised amount expressed in the reference currency");
|
||||
public static final Tag APP_REFERENCE_CURRENCY = new TagImpl("9f3b", TagValueType.NUMERIC, "Application Reference Currency", "1–4 currency codes used between the terminal and the ICC when the Transaction Currency Code is different from the Application Currency Code; each code is 3 digits according to ISO 4217");
|
||||
public static final Tag TRANSACTION_REFERENCE_CURRENCY_CODE = new TagImpl("9f3c", TagValueType.NUMERIC, "Transaction Reference Currency Code", "Code defining the common currency used by the terminal in case the Transaction Currency Code is different from the Application Currency Code");
|
||||
public static final Tag TRANSACTION_REFERENCE_CURRENCY_EXP = new TagImpl("9f3d", TagValueType.NUMERIC, "Transaction Reference Currency Exponent", "Indicates the implied position of the decimal point from the right of the transaction amount, with the Transaction Reference Currency Code represented according to ISO 4217");
|
||||
public static final Tag ADDITIONAL_TERMINAL_CAPABILITIES = new TagImpl("9f40", TagValueType.BINARY, "Additional Terminal Capabilities", "Indicates the data input and output capabilities of the terminal");
|
||||
public static final Tag TRANSACTION_SEQUENCE_COUNTER = new TagImpl("9f41", TagValueType.NUMERIC, "Transaction Sequence Counter", "Counter maintained by the terminal that is incremented by one for each transaction");
|
||||
public static final Tag APPLICATION_CURRENCY_CODE = new TagImpl("9f42", TagValueType.NUMERIC, "Application Currency Code", "Indicates the currency in which the account is managed according to ISO 4217");
|
||||
public static final Tag APP_REFERENCE_CURRECY_EXPONENT = new TagImpl("9f43", TagValueType.NUMERIC, "Application Reference Currency Exponent", "Indicates the implied position of the decimal point from the right of the amount, for each of the 1–4 reference currencies represented according to ISO 4217");
|
||||
public static final Tag APP_CURRENCY_EXPONENT = new TagImpl("9f44", TagValueType.NUMERIC, "Application Currency Exponent", "Indicates the implied position of the decimal point from the right of the amount represented according to ISO 4217");
|
||||
public static final Tag DATA_AUTHENTICATION_CODE = new TagImpl("9f45", TagValueType.BINARY, "Data Authentication Code", "An issuer assigned value that is retained by the terminal during the verification process of the Signed Static Application Data");
|
||||
public static final Tag ICC_PUBLIC_KEY_CERT = new TagImpl("9f46", TagValueType.BINARY, "ICC Public Key Certificate", "ICC Public Key certified by the issuer");
|
||||
public static final Tag ICC_PUBLIC_KEY_EXP = new TagImpl("9f47", TagValueType.BINARY, "ICC Public Key Exponent", "ICC Public Key Exponent used for the verification of the Signed Dynamic Application Data");
|
||||
public static final Tag ICC_PUBLIC_KEY_REMAINDER = new TagImpl("9f48", TagValueType.BINARY, "ICC Public Key Remainder", "Remaining digits of the ICC Public Key Modulus");
|
||||
public static final Tag DDOL = new TagImpl("9f49", TagValueType.DOL, "Dynamic Data Authentication Data Object List (DDOL)", "List of data objects (tag and length) to be passed to the ICC in the INTERNAL AUTHENTICATE command");
|
||||
public static final Tag SDA_TAG_LIST = new TagImpl("9f4a", TagValueType.BINARY, "Static Data Authentication Tag List", "List of tags of primitive data objects defined in this specification whose value fields are to be included in the Signed Static or Dynamic Application Data");
|
||||
public static final Tag SIGNED_DYNAMIC_APPLICATION_DATA = new TagImpl("9f4b", TagValueType.BINARY, "Signed Dynamic Application Data", "Digital signature on critical application parameters for DDA or CDA");
|
||||
public static final Tag ICC_DYNAMIC_NUMBER = new TagImpl("9f4c", TagValueType.BINARY, "ICC Dynamic Number", "Time-variant number generated by the ICC, to be captured by the terminal");
|
||||
public static final Tag LOG_ENTRY = new TagImpl("9f4d", TagValueType.BINARY, "Log Entry", "Provides the SFI of the Transaction Log file and its number of records");
|
||||
public static final Tag MERCHANT_NAME_AND_LOCATION = new TagImpl("9f4e", TagValueType.TEXT, "Merchant Name and Location", "Indicates the name and location of the merchant");
|
||||
public static final Tag LOG_FORMAT = new TagImpl("9f4f", TagValueType.DOL, "Log Format", "List (in tag and length format) of data objects representing the logged data elements that are passed to the terminal when a transaction log record is read");
|
||||
|
||||
public static final Tag FCI_ISSUER_DISCRETIONARY_DATA = new TagImpl("bf0c", TagValueType.BINARY, "File Control Information (FCI) Issuer Discretionary Data", "Issuer discretionary part of the FCI (e.g. O/S Manufacturer proprietary data)");
|
||||
|
||||
public static final Tag TRACK1_DATA = new TagImpl("56", TagValueType.BINARY, "Track 1 Data", "Track 1 Data contains the data objects of the track 1 according to [ISO/IEC 7813] Structure B, excluding start sentinel, end sentinel and LRC.");
|
||||
|
||||
public static final Tag TERMINAL_TRANSACTION_QUALIFIERS = new TagImpl("9f66", TagValueType.BINARY, "Terminal Transaction Qualifiers", "Provided by the reader in the GPO command and used by the card to determine processing choices based on reader functionality");
|
||||
|
||||
public static final Tag TRACK2_DATA = new TagImpl("9f6b", TagValueType.BINARY, "Track 2 Data", "Track 2 Data contains the data objects of the track 2 according to [ISO/IEC 7813] Structure B, excluding start sentinel, end sentinel and LRC.");
|
||||
public static final Tag VLP_ISSUER_AUTHORISATION_CODE = new TagImpl("9f6e", TagValueType.BINARY, "Visa Low-Value Payment (VLP) Issuer Authorisation Code", "");
|
||||
|
||||
public static final Tag EXTENDED_SELECTION = new TagImpl("9f29", TagValueType.BINARY, "Indicates the card's preference for the kernel on which the contactless application can be processed", "");
|
||||
public static final Tag KERNEL_IDENTIFIER = new TagImpl("9f2a", TagValueType.BINARY, "The value to be appended to the ADF Name in the data field of the SELECT command, if the Extended Selection Support flag is present and set to 1", "");
|
||||
|
||||
public static Tag getNotNull(byte[] tagBytes) {
|
||||
Tag tag = find(tagBytes);
|
||||
if (tag == null) {
|
||||
tag = createUnknownTag(tagBytes);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static Tag createUnknownTag(byte[] tagBytes) {
|
||||
return new TagImpl(tagBytes, TagValueType.BINARY, "[UNKNOWN TAG]", "");
|
||||
}
|
||||
|
||||
public static Tag find(byte[] tagBytes) {
|
||||
return (Tag) tags.get(ByteArrayWrapper.wrapperAround(tagBytes));
|
||||
}
|
||||
|
||||
private static void addTag(Tag tag) {
|
||||
ByteArrayWrapper baw = ByteArrayWrapper.wrapperAround(tag.getTagBytes());
|
||||
if (tags.containsKey(baw)) {
|
||||
throw new IllegalArgumentException("Tag already added " + tag);
|
||||
}
|
||||
tags.put(baw, tag);
|
||||
}
|
||||
|
||||
static {
|
||||
Field[] fields = EMVTags.class.getFields();
|
||||
for (Field f : fields) {
|
||||
if (f.getType() == Tag.class) {
|
||||
try {
|
||||
Tag t = (Tag) f.get(null);
|
||||
addTag(t);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addPaymentSystemTag(Util.fromHexString("A000000315"), new TagImpl("c1", TagValueType.BINARY, "?", "Example: BER-TLV[c1, 02 (raw 02), 1101]"));
|
||||
}
|
||||
|
||||
private static void addIssuerTag(IssuerIdentificationNumber iin, Tag tag) {
|
||||
ByteArrayWrapper tagBytesWrapped = ByteArrayWrapper.wrapperAround(tag.getTagBytes());
|
||||
LinkedHashMap<ByteArrayWrapper, Tag> issuerTags = (LinkedHashMap) issuerToTagsMap.get(iin);
|
||||
if (issuerTags == null) {
|
||||
issuerTags = new LinkedHashMap<ByteArrayWrapper, Tag>();
|
||||
issuerToTagsMap.put(iin, issuerTags);
|
||||
}
|
||||
if (issuerTags.containsKey(tagBytesWrapped)) {
|
||||
throw new IllegalArgumentException("Tag already added " + tag);
|
||||
}
|
||||
issuerTags.put(tagBytesWrapped, tag);
|
||||
}
|
||||
|
||||
private static void addPaymentSystemTag(byte[] ridBytes, Tag tag) {
|
||||
ByteArrayWrapper tagBytesWrapped = ByteArrayWrapper.wrapperAround(tag.getTagBytes());
|
||||
ByteArrayWrapper ridBytesWrapped = ByteArrayWrapper.wrapperAround(ridBytes);
|
||||
LinkedHashMap<ByteArrayWrapper, Tag> paymentSystemTags = (LinkedHashMap) paymentSystemToTagsMap.get(ridBytesWrapped);
|
||||
if (paymentSystemTags == null) {
|
||||
paymentSystemTags = new LinkedHashMap<ByteArrayWrapper, Tag>();
|
||||
paymentSystemToTagsMap.put(ridBytesWrapped, paymentSystemTags);
|
||||
}
|
||||
if (paymentSystemTags.containsKey(tagBytesWrapped)) {
|
||||
throw new IllegalArgumentException("Tag already added " + tag);
|
||||
}
|
||||
paymentSystemTags.put(tagBytesWrapped, tag);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(find(new byte[]{66}));
|
||||
System.out.println(find(new byte[]{95, 32}));
|
||||
System.out.println(getNotNull(new byte[]{95, 33}));
|
||||
}
|
||||
|
||||
public static Iterator iterator() {
|
||||
return tags.values().iterator();
|
||||
}
|
||||
|
||||
private EMVTags() {
|
||||
throw new UnsupportedOperationException("Not allowed to instantiate");
|
||||
}
|
||||
}
|
||||
59
modules/core/src/sasc/emv/IssuerIdentificationNumber.java
Normal file
59
modules/core/src/sasc/emv/IssuerIdentificationNumber.java
Normal file
@ -0,0 +1,59 @@
|
||||
package sasc.emv;
|
||||
|
||||
import java.util.Arrays;
|
||||
import sasc.util.Util;
|
||||
|
||||
public class IssuerIdentificationNumber {
|
||||
|
||||
byte[] iinBytes;
|
||||
|
||||
public IssuerIdentificationNumber(byte[] iinBytes) {
|
||||
if (iinBytes == null) {
|
||||
throw new NullPointerException("Param iinBytes cannot be null");
|
||||
}
|
||||
if (iinBytes.length != 3) {
|
||||
throw new IllegalArgumentException("Param iinBytes must have length 3, but was " + iinBytes.length);
|
||||
}
|
||||
this.iinBytes = Arrays.copyOf(iinBytes, iinBytes.length);
|
||||
}
|
||||
|
||||
public IssuerIdentificationNumber(int iin) {
|
||||
if (iin < 0 || iin > 1000000) {
|
||||
throw new IllegalArgumentException("IIN must be between 0 and 999999, but was " + iin);
|
||||
}
|
||||
byte[] tmp = Util.intToBinaryEncodedDecimalByteArray(iin);
|
||||
if (tmp.length != 6) {
|
||||
this.iinBytes = Util.resizeArray(tmp, 6);
|
||||
} else {
|
||||
this.iinBytes = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return Util.binaryHexCodedDecimalToInt(this.iinBytes);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return Arrays.copyOf(this.iinBytes, this.iinBytes.length);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof IssuerIdentificationNumber)) {
|
||||
return false;
|
||||
}
|
||||
IssuerIdentificationNumber that = (IssuerIdentificationNumber) obj;
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
if (Arrays.equals(getBytes(), that.getBytes())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
return 97 * hash + Arrays.hashCode(this.iinBytes);
|
||||
}
|
||||
}
|
||||
125
modules/core/src/sasc/iso7816/BERTLV.java
Normal file
125
modules/core/src/sasc/iso7816/BERTLV.java
Normal file
@ -0,0 +1,125 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import sasc.util.Util;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class BERTLV
|
||||
{
|
||||
private Tag tag;
|
||||
private byte[] rawEncodedLengthBytes;
|
||||
private byte[] valueBytes;
|
||||
private int length;
|
||||
|
||||
public BERTLV(Tag tag, int length, byte[] rawEncodedLengthBytes, byte[] valueBytes) {
|
||||
if (length != valueBytes.length)
|
||||
{
|
||||
throw new IllegalArgumentException("length != bytes.length");
|
||||
}
|
||||
this.tag = tag;
|
||||
this.rawEncodedLengthBytes = rawEncodedLengthBytes;
|
||||
this.valueBytes = valueBytes;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public BERTLV(Tag tag, byte[] valueBytes) {
|
||||
this.tag = tag;
|
||||
this.rawEncodedLengthBytes = encodeLength(valueBytes.length);
|
||||
this.valueBytes = valueBytes;
|
||||
this.length = valueBytes.length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static byte[] encodeLength(int length) {
|
||||
if (length <= 127)
|
||||
return new byte[] { (byte)length };
|
||||
if (length <= 255) {
|
||||
return new byte[] { -127, (byte)length };
|
||||
}
|
||||
|
||||
|
||||
if (length <= 65535) {
|
||||
return new byte[] { -126, (byte)(length >> 8), (byte)length };
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (length <= 16777215) {
|
||||
return new byte[] { -125, (byte)(length >> 16), (byte)(length >> 8), (byte)length };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
throw new IllegalArgumentException("SEQUENCE too long");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public byte[] getTagBytes() { return this.tag.getTagBytes(); }
|
||||
|
||||
|
||||
|
||||
public byte[] getRawEncodedLengthBytes() { return this.rawEncodedLengthBytes; }
|
||||
|
||||
|
||||
|
||||
public byte[] getValueBytes() { return this.valueBytes; }
|
||||
|
||||
|
||||
|
||||
public ByteArrayInputStream getValueStream() { return new ByteArrayInputStream(this.valueBytes); }
|
||||
|
||||
|
||||
public byte[] toBERTLVByteArray() {
|
||||
byte[] tagBytes = this.tag.getTagBytes();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream(tagBytes.length + this.rawEncodedLengthBytes.length + this.valueBytes.length);
|
||||
stream.write(tagBytes, 0, tagBytes.length);
|
||||
stream.write(this.rawEncodedLengthBytes, 0, this.rawEncodedLengthBytes.length);
|
||||
stream.write(this.valueBytes, 0, this.valueBytes.length);
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String toString() { return "BER-TLV[" + Util.byteArrayToHexString(getTagBytes()) + ", " + Util.int2Hex(this.length) + " (raw " + Util.byteArrayToHexString(this.rawEncodedLengthBytes) + "), " + Util.byteArrayToHexString(this.valueBytes) + "]"; }
|
||||
|
||||
|
||||
|
||||
public Tag getTag() { return this.tag; }
|
||||
|
||||
|
||||
|
||||
public int getLength() { return this.length; }
|
||||
|
||||
public static void main(String[] args) throws Exception {}
|
||||
}
|
||||
33
modules/core/src/sasc/iso7816/SmartCardException.java
Normal file
33
modules/core/src/sasc/iso7816/SmartCardException.java
Normal file
@ -0,0 +1,33 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class SmartCardException
|
||||
extends RuntimeException
|
||||
{
|
||||
public SmartCardException(String message) { super(message); }
|
||||
|
||||
|
||||
|
||||
public SmartCardException(String message, Throwable cause) { super(message, cause); }
|
||||
|
||||
|
||||
public SmartCardException(Throwable cause) { super(cause); }
|
||||
}
|
||||
33
modules/core/src/sasc/iso7816/TLVException.java
Normal file
33
modules/core/src/sasc/iso7816/TLVException.java
Normal file
@ -0,0 +1,33 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class TLVException
|
||||
extends SmartCardException
|
||||
{
|
||||
public TLVException(String message) { super(message); }
|
||||
|
||||
|
||||
|
||||
public TLVException(String message, Throwable cause) { super(message, cause); }
|
||||
|
||||
|
||||
public TLVException(Throwable cause) { super(cause); }
|
||||
}
|
||||
275
modules/core/src/sasc/iso7816/TLVUtil.java
Normal file
275
modules/core/src/sasc/iso7816/TLVUtil.java
Normal file
@ -0,0 +1,275 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import sasc.emv.EMVTags;
|
||||
import sasc.util.Util;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class TLVUtil
|
||||
{
|
||||
private static Tag searchTagById(byte[] tagIdBytes) { return EMVTags.getNotNull(tagIdBytes); }
|
||||
|
||||
|
||||
|
||||
private static Tag searchTagById(ByteArrayInputStream stream) { return searchTagById(readTagIdBytes(stream)); }
|
||||
|
||||
|
||||
|
||||
public static String getFormattedTagAndLength(byte[] data, int indentLength) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
String indent = Util.getSpaces(indentLength);
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(data);
|
||||
|
||||
boolean firstLine = true;
|
||||
while (stream.available() > 0) {
|
||||
if (firstLine) {
|
||||
firstLine = false;
|
||||
} else {
|
||||
buf.append("\n");
|
||||
}
|
||||
buf.append(indent);
|
||||
|
||||
Tag tag = searchTagById(stream);
|
||||
int length = readTagLength(stream);
|
||||
|
||||
buf.append(Util.prettyPrintHex(tag.getTagBytes()));
|
||||
buf.append(" ");
|
||||
buf.append(Util.byteArrayToHexString(Util.intToByteArray(length)));
|
||||
buf.append(" -- ");
|
||||
buf.append(tag.getName());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static byte[] readTagIdBytes(ByteArrayInputStream stream) {
|
||||
ByteArrayOutputStream tagBAOS = new ByteArrayOutputStream();
|
||||
byte tagFirstOctet = (byte)stream.read();
|
||||
tagBAOS.write(tagFirstOctet);
|
||||
|
||||
|
||||
byte MASK = 31;
|
||||
if ((tagFirstOctet & MASK) == MASK) {
|
||||
byte tlvIdNextOctet;
|
||||
do {
|
||||
int nextOctet = stream.read();
|
||||
if (nextOctet < 0) {
|
||||
break;
|
||||
}
|
||||
tlvIdNextOctet = (byte)nextOctet;
|
||||
|
||||
tagBAOS.write(tlvIdNextOctet);
|
||||
}
|
||||
while (Util.isBitSet(tlvIdNextOctet, 8) && (!Util.isBitSet(tlvIdNextOctet, 8) || (tlvIdNextOctet & 0x7F) != 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
return tagBAOS.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static int readTagLength(ByteArrayInputStream stream) {
|
||||
int length, tmpLength = stream.read();
|
||||
|
||||
if (tmpLength < 0) {
|
||||
throw new TLVException("Negative length: " + tmpLength);
|
||||
}
|
||||
|
||||
if (tmpLength <= 127) {
|
||||
|
||||
length = tmpLength;
|
||||
} else if (tmpLength == 128) {
|
||||
|
||||
|
||||
length = tmpLength;
|
||||
} else {
|
||||
|
||||
int numberOfLengthOctets = tmpLength & 0x7F;
|
||||
tmpLength = 0;
|
||||
for (int i = 0; i < numberOfLengthOctets; i++) {
|
||||
int nextLengthOctet = stream.read();
|
||||
if (nextLengthOctet < 0) {
|
||||
throw new TLVException("EOS when reading length bytes");
|
||||
}
|
||||
tmpLength <<= 8;
|
||||
tmpLength |= nextLengthOctet;
|
||||
}
|
||||
length = tmpLength;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
public static BERTLV getNextTLV(ByteArrayInputStream stream) {
|
||||
byte[] valueBytes;
|
||||
if (stream.available() < 2) {
|
||||
throw new TLVException("Error parsing data. Available bytes < 2 . Length=" + stream.available());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
stream.mark(0);
|
||||
int peekInt = stream.read();
|
||||
byte peekByte = (byte)peekInt;
|
||||
|
||||
while (peekInt != -1 && (peekByte == -1 || peekByte == 0)) {
|
||||
stream.mark(0);
|
||||
peekInt = stream.read();
|
||||
peekByte = (byte)peekInt;
|
||||
}
|
||||
stream.reset();
|
||||
|
||||
if (stream.available() < 2) {
|
||||
throw new TLVException("Error parsing data. Available bytes < 2 . Length=" + stream.available());
|
||||
}
|
||||
|
||||
byte[] tagIdBytes = readTagIdBytes(stream);
|
||||
|
||||
|
||||
|
||||
stream.mark(0);
|
||||
int posBefore = stream.available();
|
||||
|
||||
|
||||
int length = readTagLength(stream);
|
||||
|
||||
int posAfter = stream.available();
|
||||
stream.reset();
|
||||
byte[] lengthBytes = new byte[posBefore - posAfter];
|
||||
|
||||
if (lengthBytes.length < 1 || lengthBytes.length > 4) {
|
||||
throw new TLVException("Number of length bytes must be from 1 to 4. Found " + lengthBytes.length);
|
||||
}
|
||||
|
||||
stream.read(lengthBytes, 0, lengthBytes.length);
|
||||
|
||||
int rawLength = Util.byteArrayToInt(lengthBytes);
|
||||
|
||||
|
||||
|
||||
Tag tag = searchTagById(tagIdBytes);
|
||||
|
||||
|
||||
if (rawLength == 128) {
|
||||
|
||||
stream.mark(0);
|
||||
int prevOctet = 1;
|
||||
|
||||
int len = 0;
|
||||
while (true) {
|
||||
len++;
|
||||
int curOctet = stream.read();
|
||||
if (curOctet < 0) {
|
||||
throw new TLVException("Error parsing data. TLV length byte indicated indefinite length, but EOS was reached before 0x0000 was found" + stream
|
||||
|
||||
.available());
|
||||
}
|
||||
if (prevOctet == 0 && curOctet == 0) {
|
||||
break;
|
||||
}
|
||||
prevOctet = curOctet;
|
||||
}
|
||||
len -= 2;
|
||||
valueBytes = new byte[len];
|
||||
stream.reset();
|
||||
stream.read(valueBytes, 0, len);
|
||||
length = len;
|
||||
} else {
|
||||
if (stream.available() < length) {
|
||||
throw new TLVException("Length byte(s) indicated " + length + " value bytes, but only " + stream.available() + " " + ((stream.available() > 1) ? "are" : "is") + " available");
|
||||
}
|
||||
|
||||
valueBytes = new byte[length];
|
||||
stream.read(valueBytes, 0, length);
|
||||
}
|
||||
|
||||
|
||||
stream.mark(0);
|
||||
peekInt = stream.read();
|
||||
peekByte = (byte)peekInt;
|
||||
while (peekInt != -1 && (peekByte == -1 || peekByte == 0)) {
|
||||
stream.mark(0);
|
||||
peekInt = stream.read();
|
||||
peekByte = (byte)peekInt;
|
||||
}
|
||||
stream.reset();
|
||||
|
||||
|
||||
return new BERTLV(tag, length, lengthBytes, valueBytes);
|
||||
}
|
||||
|
||||
|
||||
private static String getTagValueAsString(Tag tag, byte[] value) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
switch (tag.getTagValueType()) {
|
||||
case TEXT:
|
||||
buf.append("=");
|
||||
buf.append(new String(value));
|
||||
break;
|
||||
case NUMERIC:
|
||||
buf.append("NUMERIC");
|
||||
break;
|
||||
case BINARY:
|
||||
buf.append("BINARY");
|
||||
break;
|
||||
case MIXED:
|
||||
buf.append("=");
|
||||
buf.append(Util.getSafePrintChars(value));
|
||||
break;
|
||||
case DOL:
|
||||
buf.append("");
|
||||
break;
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static List<TagAndLength> parseTagAndLength(byte[] data) {
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(data);
|
||||
List<TagAndLength> tagAndLengthList = new ArrayList<TagAndLength>();
|
||||
|
||||
while (stream.available() > 0) {
|
||||
if (stream.available() < 2) {
|
||||
throw new SmartCardException("Data length < 2 : " + stream.available());
|
||||
}
|
||||
byte[] tagIdBytes = readTagIdBytes(stream);
|
||||
int tagValueLength = readTagLength(stream);
|
||||
|
||||
Tag tag = searchTagById(tagIdBytes);
|
||||
|
||||
tagAndLengthList.add(new TagAndLength(tag, tagValueLength));
|
||||
}
|
||||
return tagAndLengthList;
|
||||
}
|
||||
}
|
||||
27
modules/core/src/sasc/iso7816/Tag.java
Normal file
27
modules/core/src/sasc/iso7816/Tag.java
Normal file
@ -0,0 +1,27 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
|
||||
|
||||
public interface Tag
|
||||
{
|
||||
boolean isConstructed();
|
||||
|
||||
byte[] getTagBytes();
|
||||
|
||||
String getName();
|
||||
|
||||
String getDescription();
|
||||
|
||||
TagType getType();
|
||||
|
||||
TagValueType getTagValueType();
|
||||
|
||||
Class getTagClass();
|
||||
|
||||
int getNumTagBytes();
|
||||
|
||||
public enum Class
|
||||
{
|
||||
UNIVERSAL, APPLICATION, CONTEXT_SPECIFIC, PRIVATE;
|
||||
}
|
||||
}
|
||||
51
modules/core/src/sasc/iso7816/TagAndLength.java
Normal file
51
modules/core/src/sasc/iso7816/TagAndLength.java
Normal file
@ -0,0 +1,51 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class TagAndLength
|
||||
{
|
||||
private Tag tag;
|
||||
private int length;
|
||||
|
||||
public TagAndLength(Tag tag, int length) {
|
||||
this.tag = tag;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
|
||||
public Tag getTag() { return this.tag; }
|
||||
|
||||
|
||||
|
||||
public int getLength() { return this.length; }
|
||||
|
||||
|
||||
public byte[] getBytes() {
|
||||
byte[] tagBytes = this.tag.getTagBytes();
|
||||
byte[] tagAndLengthBytes = Arrays.copyOf(tagBytes, tagBytes.length + 1);
|
||||
tagAndLengthBytes[tagAndLengthBytes.length - 1] = (byte)this.length;
|
||||
return tagAndLengthBytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String toString() { return this.tag.toString() + " length: " + this.length; }
|
||||
}
|
||||
141
modules/core/src/sasc/iso7816/TagImpl.java
Normal file
141
modules/core/src/sasc/iso7816/TagImpl.java
Normal file
@ -0,0 +1,141 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
import java.util.Arrays;
|
||||
import sasc.util.Util;
|
||||
|
||||
public class TagImpl
|
||||
implements Tag {
|
||||
|
||||
byte[] idBytes;
|
||||
String name;
|
||||
String description;
|
||||
TagValueType tagValueType;
|
||||
Tag.Class tagClass;
|
||||
TagType type;
|
||||
|
||||
public TagImpl(int idDecimal, TagValueType tagValueType) {
|
||||
String octal = Integer.toOctalString(idDecimal);
|
||||
if (octal.length() == 1) {
|
||||
octal = "0" + octal;
|
||||
} else if (octal.length() == 3) {
|
||||
octal = "8" + octal;
|
||||
}
|
||||
build(Util.fromHexString(octal), tagValueType, null, null);
|
||||
}
|
||||
|
||||
public TagImpl(String id, TagValueType tagValueType, String name, String description) {
|
||||
build(Util.fromHexString(id), tagValueType, name, description);
|
||||
}
|
||||
|
||||
public TagImpl(byte[] idBytes, TagValueType tagValueType, String name, String description) {
|
||||
build(idBytes, tagValueType, name, description);
|
||||
}
|
||||
|
||||
private void build(byte[] idBytes, TagValueType tagValueType, String name, String description) {
|
||||
if (idBytes == null) {
|
||||
throw new IllegalArgumentException("Param id cannot be null");
|
||||
}
|
||||
if (idBytes.length == 0) {
|
||||
throw new IllegalArgumentException("Param id cannot be empty");
|
||||
}
|
||||
if (tagValueType == null) {
|
||||
throw new IllegalArgumentException("Param tagValueType cannot be null");
|
||||
}
|
||||
this.idBytes = idBytes;
|
||||
this.name = (name != null) ? name : "";
|
||||
this.description = (description != null) ? description : "";
|
||||
this.tagValueType = tagValueType;
|
||||
|
||||
if (Util.isBitSet(this.idBytes[0], 6)) {
|
||||
this.type = TagType.CONSTRUCTED;
|
||||
} else {
|
||||
this.type = TagType.PRIMITIVE;
|
||||
}
|
||||
|
||||
byte classValue = (byte) (this.idBytes[0] >>> 6 & 0x3);
|
||||
switch (classValue) {
|
||||
case 0:
|
||||
this.tagClass = Tag.Class.UNIVERSAL;
|
||||
return;
|
||||
case 1:
|
||||
this.tagClass = Tag.Class.APPLICATION;
|
||||
return;
|
||||
case 2:
|
||||
this.tagClass = Tag.Class.CONTEXT_SPECIFIC;
|
||||
return;
|
||||
case 3:
|
||||
this.tagClass = Tag.Class.PRIVATE;
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("UNEXPECTED TAG CLASS: " + Util.byte2BinaryLiteral(classValue) + " " + Util.byteArrayToHexString(this.idBytes) + " " + name);
|
||||
}
|
||||
|
||||
public boolean isConstructed() {
|
||||
return (this.type == TagType.CONSTRUCTED);
|
||||
}
|
||||
|
||||
public byte[] getTagBytes() {
|
||||
return this.idBytes;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public TagValueType getTagValueType() {
|
||||
return this.tagValueType;
|
||||
}
|
||||
|
||||
public TagType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Tag.Class getTagClass() {
|
||||
return this.tagClass;
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Tag)) {
|
||||
return false;
|
||||
}
|
||||
Tag that = (Tag) other;
|
||||
if (getTagBytes().length != that.getTagBytes().length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.equals(getTagBytes(), that.getTagBytes());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
return 59 * hash + Arrays.hashCode(this.idBytes);
|
||||
}
|
||||
|
||||
public int getNumTagBytes() {
|
||||
return this.idBytes.length;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Tag[");
|
||||
sb.append(Util.byteArrayToHexString(getTagBytes()));
|
||||
sb.append("] Name=");
|
||||
sb.append(getName());
|
||||
sb.append(", TagType=");
|
||||
sb.append(getType());
|
||||
sb.append(", ValueType=");
|
||||
sb.append(getTagValueType());
|
||||
sb.append(", Class=");
|
||||
sb.append(this.tagClass);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
TagImpl tag = new TagImpl("bf0c", TagValueType.BINARY, "", "");
|
||||
System.out.println(tag);
|
||||
}
|
||||
}
|
||||
6
modules/core/src/sasc/iso7816/TagType.java
Normal file
6
modules/core/src/sasc/iso7816/TagType.java
Normal file
@ -0,0 +1,6 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
public enum TagType {
|
||||
PRIMITIVE,
|
||||
CONSTRUCTED;
|
||||
}
|
||||
5
modules/core/src/sasc/iso7816/TagValueType.java
Normal file
5
modules/core/src/sasc/iso7816/TagValueType.java
Normal file
@ -0,0 +1,5 @@
|
||||
package sasc.iso7816;
|
||||
|
||||
public enum TagValueType {
|
||||
BINARY, NUMERIC, TEXT, MIXED, DOL, TEMPLATE;
|
||||
}
|
||||
56
modules/core/src/sasc/util/ByteArrayWrapper.java
Normal file
56
modules/core/src/sasc/util/ByteArrayWrapper.java
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010 sasc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sasc.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class ByteArrayWrapper {
|
||||
|
||||
private final byte[] data;
|
||||
private final int hashcode;
|
||||
|
||||
private ByteArrayWrapper(byte[] data) {
|
||||
this.data = data;
|
||||
this.hashcode = Arrays.hashCode(data);
|
||||
}
|
||||
|
||||
public static ByteArrayWrapper wrapperAround(byte[] data) {
|
||||
if (data == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return new ByteArrayWrapper(data);
|
||||
}
|
||||
|
||||
public static ByteArrayWrapper copyOf(byte[] data) {
|
||||
if (data == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return new ByteArrayWrapper(Util.copyByteArray(data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ByteArrayWrapper)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(data, ((ByteArrayWrapper) other).data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashcode;
|
||||
}
|
||||
}
|
||||
689
modules/core/src/sasc/util/Util.java
Normal file
689
modules/core/src/sasc/util/Util.java
Normal file
@ -0,0 +1,689 @@
|
||||
/*
|
||||
* Copyright 2010 sasc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sasc.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.BitSet;
|
||||
import java.util.Date;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sasc
|
||||
*/
|
||||
public class Util {
|
||||
|
||||
private Util() {
|
||||
throw new UnsupportedOperationException("Not allowed to instantiate");
|
||||
}
|
||||
|
||||
public static String getSpaces(int length) {
|
||||
StringBuilder buf = new StringBuilder(length);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
buf.append(" ");
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(String in, int indent, boolean wrapLines) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
char c = in.charAt(i);
|
||||
buf.append(c);
|
||||
|
||||
int nextPos = i+1;
|
||||
if (wrapLines && nextPos % 32 == 0 && nextPos != in.length()) {
|
||||
buf.append("\n").append(getSpaces(indent));
|
||||
} else if (nextPos % 2 == 0 && nextPos != in.length()) {
|
||||
buf.append(" ");
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(String in, int indent){
|
||||
return prettyPrintHex(in, indent, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(byte[] data, int indent) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data), indent, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(byte[] data) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(byte[] data, int startPos, int length) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHexNoWrap(byte[] data) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, false);
|
||||
}
|
||||
|
||||
public static String prettyPrintHexNoWrap(byte[] data, int startPos, int length) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, false);
|
||||
}
|
||||
|
||||
public static String prettyPrintHexNoWrap(String in) {
|
||||
return Util.prettyPrintHex(in, 0, false);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(String in) {
|
||||
return prettyPrintHex(in, 0, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(BigInteger bi) {
|
||||
byte[] data = bi.toByteArray();
|
||||
if (data[0] == (byte) 0x00) {
|
||||
byte[] tmp = new byte[data.length - 1];
|
||||
System.arraycopy(data, 1, tmp, 0, data.length - 1);
|
||||
data = tmp;
|
||||
}
|
||||
return prettyPrintHex(data);
|
||||
}
|
||||
|
||||
public static byte[] performRSA(byte[] dataBytes, byte[] expBytes, byte[] modBytes) {
|
||||
|
||||
int inBytesLength = dataBytes.length;
|
||||
|
||||
if (expBytes[0] >= (byte) 0x80) {
|
||||
//Prepend 0x00 to modulus
|
||||
byte[] tmp = new byte[expBytes.length + 1];
|
||||
tmp[0] = (byte) 0x00;
|
||||
System.arraycopy(expBytes, 0, tmp, 1, expBytes.length);
|
||||
expBytes = tmp;
|
||||
}
|
||||
|
||||
if (modBytes[0] >= (byte) 0x80) {
|
||||
//Prepend 0x00 to modulus
|
||||
byte[] tmp = new byte[modBytes.length + 1];
|
||||
tmp[0] = (byte) 0x00;
|
||||
System.arraycopy(modBytes, 0, tmp, 1, modBytes.length);
|
||||
modBytes = tmp;
|
||||
}
|
||||
|
||||
if (dataBytes[0] >= (byte) 0x80) {
|
||||
//Prepend 0x00 to signed data to avoid that the most significant bit is interpreted as the "signed" bit
|
||||
byte[] tmp = new byte[dataBytes.length + 1];
|
||||
tmp[0] = (byte) 0x00;
|
||||
System.arraycopy(dataBytes, 0, tmp, 1, dataBytes.length);
|
||||
dataBytes = tmp;
|
||||
}
|
||||
|
||||
BigInteger exp = new BigInteger(expBytes);
|
||||
BigInteger mod = new BigInteger(modBytes);
|
||||
BigInteger data = new BigInteger(dataBytes);
|
||||
|
||||
byte[] result = data.modPow(exp, mod).toByteArray();
|
||||
|
||||
if (result.length == (inBytesLength+1) && result[0] == (byte)0x00) {
|
||||
//Remove 0x00 from beginning of array
|
||||
byte[] tmp = new byte[inBytesLength];
|
||||
System.arraycopy(result, 1, tmp, 0, inBytesLength);
|
||||
result = tmp;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] calculateSHA1(byte[] data) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
|
||||
return sha1.digest(data);
|
||||
}
|
||||
|
||||
public static String byte2Hex(byte b) {
|
||||
String[] HEX_DIGITS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
|
||||
int nb = b & 0xFF;
|
||||
int i_1 = (nb >>> 4) & 0xF;
|
||||
int i_2 = nb & 0xF;
|
||||
return HEX_DIGITS[i_1] + HEX_DIGITS[i_2];
|
||||
}
|
||||
|
||||
public static String short2Hex(short s) {
|
||||
byte b1 = (byte) (s >>> 8);
|
||||
byte b2 = (byte) (s & 0xFF);
|
||||
return byte2Hex(b1) + byte2Hex(b2);
|
||||
}
|
||||
|
||||
public static int byteToInt(byte b) {
|
||||
return (int) b & 0xFF;
|
||||
}
|
||||
|
||||
public static int byteToInt(byte first, byte second) {
|
||||
int value = (first & 0xFF) << 8;
|
||||
value += second & 0xFF;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static short byte2Short(byte b1, byte b2) {
|
||||
return (short) ((b1 << 8) | (b2 & 0xFF));
|
||||
}
|
||||
|
||||
public static String getFormattedNanoTime(long nano) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append((int) (nano / 1000000));
|
||||
buf.append("ms ");
|
||||
buf.append(nano % 1000000);
|
||||
buf.append("ns");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static byte[] getCurrentDateAsNumericEncodedByteArray(){
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyMMdd");
|
||||
return fromHexString(format.format(new Date()));
|
||||
}
|
||||
|
||||
//This prints all non-control characters common to all parts of ISO/IEC 8859
|
||||
//See EMV book 4 Annex B: Table 36: Common Character Set
|
||||
public static String getSafePrintChars(byte[] byteArray) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
}
|
||||
return getSafePrintChars(byteArray, 0, byteArray.length);
|
||||
}
|
||||
|
||||
public static String getSafePrintChars(byte[] byteArray, int startPos, int length) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
}
|
||||
if(byteArray.length < startPos+length){
|
||||
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")");
|
||||
}
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = startPos; i < startPos+length; i++) {
|
||||
if (byteArray[i] >= (byte) 0x20 && byteArray[i] < (byte) 0x7F) {
|
||||
buf.append((char) byteArray[i]);
|
||||
} else {
|
||||
buf.append(".");
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array into a hex string.
|
||||
* @param byteArray the byte array source
|
||||
* @return a hex string representing the byte array
|
||||
*/
|
||||
public static String byteArrayToHexString(final byte[] byteArray) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
}
|
||||
return byteArrayToHexString(byteArray, 0, byteArray.length);
|
||||
}
|
||||
|
||||
public static String byteArrayToHexString(final byte[] byteArray, int startPos, int length) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
}
|
||||
if(byteArray.length < startPos+length){
|
||||
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")");
|
||||
}
|
||||
StringBuilder hexData = new StringBuilder();
|
||||
int onebyte;
|
||||
for (int i = 0; i < length; i++) {
|
||||
onebyte = ((0x000000ff & byteArray[startPos+i]) | 0xffffff00);
|
||||
hexData.append(Integer.toHexString(onebyte).substring(6));
|
||||
}
|
||||
return hexData.toString();
|
||||
}
|
||||
|
||||
public static String int2Hex(int i) {
|
||||
String hex = Integer.toHexString(i);
|
||||
if (hex.length() % 2 != 0) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
public static String int2HexZeroPad(int i) {
|
||||
String hex = int2Hex(i);
|
||||
if (hex.length() % 2 != 0) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
/**
|
||||
* The length of the returned array depends on the size of the int
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToByteArray(int value) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
byte one = (byte) (value >>> 24);
|
||||
byte two = (byte) (value >>> 16);
|
||||
byte three = (byte) (value >>> 8);
|
||||
byte four = (byte) (value);
|
||||
|
||||
boolean found = false;
|
||||
|
||||
if (one > 0x00) {
|
||||
baos.write(one);
|
||||
found = true;
|
||||
}
|
||||
if (found || two > 0x00) {
|
||||
baos.write(two);
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found || three > 0x00) {
|
||||
baos.write(three);
|
||||
}
|
||||
|
||||
baos.write(four);
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array with length = 4
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToByteArray4(int value) {
|
||||
return new byte[]{
|
||||
(byte) (value >>> 24),
|
||||
(byte) (value >>> 16),
|
||||
(byte) (value >>> 8),
|
||||
(byte) value};
|
||||
}
|
||||
|
||||
public static int byteArrayToInt(byte[] byteArray) {
|
||||
return byteArrayToInt(byteArray, 0, byteArray.length);
|
||||
}
|
||||
|
||||
public static int byteArrayToInt(byte[] byteArray, int startPos, int length) {
|
||||
if (byteArray == null) {
|
||||
throw new IllegalArgumentException("Parameter 'byteArray' cannot be null");
|
||||
}
|
||||
if (length <= 0 || length > 4) {
|
||||
throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length);
|
||||
}
|
||||
if (length == 4 && Util.isBitSet(byteArray[startPos], 8)){
|
||||
throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos]));
|
||||
}
|
||||
int value = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
value += ((byteArray[startPos+i] & 0xFF) << 8 * (length - i - 1));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static long byteArrayToLong(byte[] byteArray, int startPos, int length) {
|
||||
if (byteArray == null) {
|
||||
throw new IllegalArgumentException("Parameter 'byteArray' cannot be null");
|
||||
}
|
||||
if (length <= 0 || length > 8) {
|
||||
throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length);
|
||||
}
|
||||
if (length == 8 && Util.isBitSet(byteArray[startPos], 8)){
|
||||
throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos]));
|
||||
}
|
||||
long value = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
value += ((byteArray[startPos+i] & (long)0xFF) << 8 * (length - i - 1));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static byte[] fromHexString(String encoded) {
|
||||
encoded = removeSpaces(encoded);
|
||||
if (encoded.length() == 0){
|
||||
return new byte[0];
|
||||
}
|
||||
if ((encoded.length() % 2) != 0) {
|
||||
throw new IllegalArgumentException("Input string must contain an even number of characters: "+encoded);
|
||||
}
|
||||
final byte result[] = new byte[encoded.length() / 2];
|
||||
final char enc[] = encoded.toCharArray();
|
||||
for (int i = 0; i < enc.length; i += 2) {
|
||||
StringBuilder curr = new StringBuilder(2);
|
||||
curr.append(enc[i]).append(enc[i + 1]);
|
||||
result[i / 2] = (byte) Integer.parseInt(curr.toString(), 16);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String removeCRLFTab(String s) {
|
||||
StringTokenizer st = new StringTokenizer(s, "\r\n\t", false);
|
||||
StringBuilder buf = new StringBuilder();
|
||||
while (st.hasMoreElements()) {
|
||||
buf.append(st.nextElement());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String removeSpaces(String s) {
|
||||
return s.replaceAll(" ", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary Coded Decimal (BCD)
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToBinaryEncodedDecimalByteArray(int val){
|
||||
String str = String.valueOf(val);
|
||||
if(str.length() % 2 != 0){
|
||||
str = "0"+str;
|
||||
}
|
||||
return Util.fromHexString(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts the literal hex representation of a byte to an int.
|
||||
* eg 0x70 = 70 (int)
|
||||
* @param b
|
||||
*/
|
||||
public static int binaryCodedDecimalToInt(byte b) {
|
||||
String hex = Util.byte2Hex(b);
|
||||
try {
|
||||
return Integer.parseInt(hex);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("The hex representation of argument b must be digits only, and integer", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts the literal hex representation of a decimal
|
||||
* encoded in 1-5 bytes to an int.
|
||||
* The value should not be larger than Integer.MAX_VALUE
|
||||
*
|
||||
* eg 0x70 = 70 (decimal)
|
||||
* eg 0x21 47 48 36 47 = 2147483647 (decimal)
|
||||
* @param hex
|
||||
*/
|
||||
public static int binaryHexCodedDecimalToInt(String hex) {
|
||||
if (hex == null) {
|
||||
throw new IllegalArgumentException("Param hex cannot be null");
|
||||
}
|
||||
hex = Util.removeSpaces(hex);
|
||||
if (hex.length() > 10) {
|
||||
throw new IllegalArgumentException("There must be a maximum of 5 hex octets. hex=" + hex);
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(hex);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("Argument hex must be all digits. hex="+hex, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts a 1-5 byte BCD to an int.
|
||||
* eg 0x7099 = 7099 (int)
|
||||
* @param bcdArray
|
||||
*/
|
||||
public static int binaryHexCodedDecimalToInt(byte[] bcdArray) {
|
||||
if (bcdArray == null) {
|
||||
throw new IllegalArgumentException("Param bcdArray cannot be null");
|
||||
}
|
||||
return binaryHexCodedDecimalToInt(Util.byteArrayToHexString(bcdArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a String with length = 8
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public static String byte2BinaryLiteral(byte val) {
|
||||
String s = Integer.toBinaryString(Util.byteToInt(val));
|
||||
if (s.length() < 8) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < 8 - s.length(); i++) {
|
||||
sb.append('0');
|
||||
}
|
||||
sb.append(s);
|
||||
s = sb.toString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bitset containing the values in bytes.
|
||||
* The byte-ordering of bytes must be big-endian which means the most significant bit is in element 0.
|
||||
*
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static BitSet byteArray2BitSet(byte[] bytes) {
|
||||
BitSet bits = new BitSet();
|
||||
for (int i = 0; i < bytes.length * 8; i++) {
|
||||
if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
|
||||
bits.set(i);
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
/* Returns a byte array of at least length 1.
|
||||
* The most significant bit in the result is guaranteed not to be a 1
|
||||
* (since BitSet does not support sign extension).
|
||||
* The byte-ordering of the result is big-endian which means the most significant bit is in element 0.
|
||||
* The bit at index 0 of the bit set is assumed to be the least significant bit.
|
||||
*/
|
||||
public static byte[] bitSet2ByteArray(BitSet bits) {
|
||||
byte[] bytes = new byte[bits.length() / 8 + 1];
|
||||
for (int i = 0; i < bits.length(); i++) {
|
||||
if (bits.get(i)) {
|
||||
bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @param bitPos The leftmost bit is 8 (the most significant bit)
|
||||
* @return
|
||||
*/
|
||||
public static boolean isBitSet(byte val, int bitPos) {
|
||||
if (bitPos < 1 || bitPos > 8) {
|
||||
throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos);
|
||||
}
|
||||
if ((val >>> (bitPos - 1) & 0x1) == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// /**
|
||||
// *
|
||||
// * @param val
|
||||
// * @return
|
||||
// */
|
||||
// public static int getBitsSetCount(byte val) {
|
||||
// int numBitsSet = 0;
|
||||
// for(int i=1; i<=8; i++){
|
||||
// if(Util.isBitSet(val, i)){
|
||||
// numBitsSet++;
|
||||
// }
|
||||
// }
|
||||
// return numBitsSet;
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param bitPos The leftmost bit is 8
|
||||
* @param on
|
||||
* @return
|
||||
*/
|
||||
public static byte setBit(byte data, int bitPos, boolean on) {
|
||||
if (bitPos < 1 || bitPos > 8) {
|
||||
throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos);
|
||||
}
|
||||
if (on) { //Set bit
|
||||
return data |= 1 << (bitPos - 1);
|
||||
} else { //Unset bit
|
||||
return data &= ~(1 << (bitPos - 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] generateRandomBytes(int numBytes){
|
||||
//TODO get bytes from a hardware RNG, or set seed
|
||||
byte[] rndBytes = new byte[numBytes];
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.nextBytes(rndBytes);
|
||||
return rndBytes;
|
||||
}
|
||||
|
||||
public static InputStream loadResource(Class cls, String path){
|
||||
return cls.getResourceAsStream(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified array, prepending 0x00, or cutting off MSBytes if necessary
|
||||
* @param original
|
||||
* @param newLength
|
||||
* @return
|
||||
*/
|
||||
public static byte[] resizeArray(byte[] original, int newLength) {
|
||||
if(original == null){
|
||||
throw new IllegalArgumentException("byte array cannot be null");
|
||||
}
|
||||
if(newLength < 0){
|
||||
throw new IllegalArgumentException("Illegal new length: "+newLength+". Must be >= 0");
|
||||
}
|
||||
if(newLength == 0){
|
||||
return new byte[0];
|
||||
}
|
||||
byte[] tmp = new byte[newLength];
|
||||
|
||||
int srcPos = tmp.length > original.length ? 0 : original.length - tmp.length;
|
||||
int destPos = tmp.length > original.length ? tmp.length - original.length : 0;
|
||||
int length = tmp.length > original.length ? original.length : tmp.length;
|
||||
|
||||
System.arraycopy(original, srcPos, tmp, destPos, length);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public static byte[] copyByteArray(byte[] array2Copy){
|
||||
if (array2Copy == null) {
|
||||
//return new byte[0] instead?
|
||||
throw new IllegalArgumentException("Argument 'array2Copy' cannot be null");
|
||||
}
|
||||
return copyByteArray(array2Copy, 0, array2Copy.length);
|
||||
}
|
||||
|
||||
public static byte[] copyByteArray(byte[] array2Copy, int startPos, int length){
|
||||
if (array2Copy == null) {
|
||||
//return new byte[0] instead?
|
||||
throw new IllegalArgumentException("Argument 'array2Copy' cannot be null");
|
||||
}
|
||||
if(array2Copy.length < startPos+length){
|
||||
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+array2Copy.length+")");
|
||||
}
|
||||
byte[] copy = new byte[array2Copy.length];
|
||||
System.arraycopy(array2Copy, startPos, copy, 0, length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static String getStackTrace(Throwable t){
|
||||
StringWriter sw = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
public static String readInputStreamToString(InputStream is, String encoding) throws IOException {
|
||||
InputStreamReader input = new InputStreamReader(is, encoding);
|
||||
final int CHARS_PER_PAGE = 5000; //counting spaces
|
||||
final char[] buffer = new char[CHARS_PER_PAGE];
|
||||
StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
|
||||
for (int read = input.read(buffer, 0, buffer.length);
|
||||
read != -1;
|
||||
read = input.read(buffer, 0, buffer.length)) {
|
||||
output.append(buffer, 0, read);
|
||||
}
|
||||
|
||||
String text = output.toString();
|
||||
return text;
|
||||
}
|
||||
|
||||
public static void writeStringToFile(String string, String fileName, boolean append) throws IOException {
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(fileName, append));
|
||||
out.write(string);
|
||||
out.close();
|
||||
}
|
||||
|
||||
public static Class getCallerClass(int i) {
|
||||
Class[] classContext = new SecurityManager() {
|
||||
@Override public Class[] getClassContext() {
|
||||
return super.getClassContext();
|
||||
}
|
||||
}.getClassContext();
|
||||
if (classContext != null) {
|
||||
for (int j = 0; j < classContext.length; j++) {
|
||||
if (classContext[j] == Util.class) {
|
||||
return classContext[i+j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SecurityManager.getClassContext() returns null on Android 4.0
|
||||
try {
|
||||
StackTraceElement[] classNames = Thread.currentThread().getStackTrace();
|
||||
for (int j = 0; j < classNames.length; j++) {
|
||||
if (Class.forName(classNames[j].getClassName()) == Util.class) {
|
||||
return Class.forName(classNames[i+j].getClassName());
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// System.out.println(Util.isBitSet((byte) 0x5f, 2)); // 0101 1111
|
||||
// System.out.println(Util.isBitSet((byte) 0x9f, 2)); // 1001 1111
|
||||
//
|
||||
// System.out.println(Util.byte2Short((byte) 0x6F, (byte) 0xEF));
|
||||
// System.out.println(Util.short2Hex(Util.byte2Short((byte) 0x6F, (byte) 0xEF)));
|
||||
//
|
||||
// System.out.println(Util.byteArrayToInt(new byte[]{(byte) 0x6F, (byte) 0xEF}));
|
||||
// System.out.println(Util.byteArrayToHexString(Util.intToByteArray(28655)));
|
||||
//
|
||||
// System.out.println(Util.byte2BinaryLiteral((byte) 0x00));
|
||||
// System.out.println(Util.byte2BinaryLiteral((byte) 0x3F));
|
||||
// System.out.println(Util.byte2BinaryLiteral((byte) 0x80));
|
||||
// System.out.println(Util.byte2BinaryLiteral((byte) 0xAA));
|
||||
// System.out.println(Util.byte2BinaryLiteral((byte) 0xFF));
|
||||
//
|
||||
// System.out.println(Util.byte2BinaryLiteral((byte) 0x8A));
|
||||
// System.out.println(Util.byte2BinaryLiteral(Util.setBit((byte) 0x8A, 5, true)));
|
||||
// System.out.println(Util.byte2BinaryLiteral(Util.setBit((byte) 0x8A, 8, false)));
|
||||
//
|
||||
// System.out.println(Util.byteArrayToLong(Util.fromHexString("7f ff ff ff ff ff ff ff"), 0, 8));
|
||||
// System.out.println(Util.byteArrayToLong(Util.fromHexString("22 18 09 04 0b 00 e0 30 23 07 00 00 00 42 d2 85 4e 23 07 00 00 00 00 21 69 42"), 13, 4));
|
||||
|
||||
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 0)));
|
||||
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 1)));
|
||||
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 2)));
|
||||
|
||||
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01, 0x02}, 1)));
|
||||
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01, 0x02}, 4)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user