Performance tuning - backend

This commit is contained in:
2026-07-21 21:32:53 +05:30
parent 4f7c06ed97
commit f0481d090c
14 changed files with 330 additions and 139 deletions

View File

@@ -3,7 +3,11 @@
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://jakarta.ee/xml/ns/jakartaee"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://jakarta.ee/xml/ns/jakartaee">
<display-name>Matrix Cygnus 1.0</display-name> <display-name>Matrix Cygnus 1.0</display-name>
<listener>
<listener-class>matrix.nimble.utilities.DatabasePoolLifecycleListener</listener-class>
</listener>
<!-- The front controller of the Spring MVC Web application, responsible <!-- The front controller of the Spring MVC Web application, responsible
for handling all application requests --> for handling all application requests -->

View File

@@ -21,6 +21,7 @@
<poi.version>5.5.1</poi.version> <poi.version>5.5.1</poi.version>
<itext.version>5.3.4</itext.version> <itext.version>5.3.4</itext.version>
<log4j.version>2.24.3</log4j.version> <log4j.version>2.24.3</log4j.version>
<hikaricp.version>6.3.3</hikaricp.version>
<junit.version>5.12.2</junit.version> <junit.version>5.12.2</junit.version>
<tomcat.version>10.1.57</tomcat.version> <tomcat.version>10.1.57</tomcat.version>
<server.port>8080</server.port> <server.port>8080</server.port>
@@ -123,6 +124,11 @@
<version>42.7.13</version> <version>42.7.13</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${hikaricp.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.microsoft.sqlserver</groupId> <groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId> <artifactId>mssql-jdbc</artifactId>

View File

@@ -324,7 +324,7 @@ public class CombinedExcelReporting {
} }
if(isProcessFlag()) if(isProcessFlag())
{ {
setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail sent successfully.\n" : "")); setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail delivery queued.\n" : ""));
} }
} }
private void DesignReport(String [] reportData,String [][] reportlayout) private void DesignReport(String [] reportData,String [][] reportlayout)
@@ -430,16 +430,12 @@ public class CombinedExcelReporting {
{ {
if(isAutoemail) if(isAutoemail)
{ {
SendReports SR=new SendReports(); if (!ReportEmailJobExecutor.submitBatch(getErrCode(), processedOn, BranchId, getUserId(), runningMode))
SR.setErrCode(getErrCode()); {
SR.setProcessFlag(true); setErrMsg(getErrCode()+"SEML:warning:Bank Reports generated successfully, but the email queue is full. Please retry from the mailbox.");
SR.SendReportsByBatch(processedOn, BranchId,getUserId(),runningMode); setErrDesc("The bounded report email queue has reached its configured capacity.");
if (!SR.isProcessFlag()) setProcessFlag(false);
{ }
setErrMsg(SR.getErrMsg().replace(getErrCode()+"SEML:warning:", getErrCode()+"SEML:warning:Bank Reports generated successfully.\n\n"));
setErrDesc(SR.getErrDesc());
setProcessFlag(SR.isProcessFlag());
}
} }
} }
} }

View File

@@ -314,7 +314,7 @@ public class CombinedReporting {
} }
if(isProcessFlag()) if(isProcessFlag())
{ {
setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail sent successfully.\n" : "")); setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail delivery queued.\n" : ""));
} }
} }
private void DesignReport(String [] reportData,String [][] reportlayout,PDFFormatter PDFF) private void DesignReport(String [] reportData,String [][] reportlayout,PDFFormatter PDFF)
@@ -482,16 +482,12 @@ public class CombinedReporting {
{ {
if(isAutoemail) if(isAutoemail)
{ {
SendReports SR=new SendReports(); if (!ReportEmailJobExecutor.submitBatch(getErrCode(), processedOn, BranchId, getUserId(), runningMode))
SR.setErrCode(getErrCode()); {
SR.setProcessFlag(true); setErrMsg(getErrCode()+"SEML:warning:Bank Reports generated successfully, but the email queue is full. Please retry from the mailbox.");
SR.SendReportsByBatch(processedOn, BranchId,getUserId(),runningMode); setErrDesc("The bounded report email queue has reached its configured capacity.");
if (!SR.isProcessFlag()) setProcessFlag(false);
{ }
setErrMsg(SR.getErrMsg().replace(getErrCode()+"SEML:warning:", getErrCode()+"SEML:warning:Bank Reports generated successfully.\n\n"));
setErrDesc(SR.getErrDesc());
setProcessFlag(SR.isProcessFlag());
}
} }
} }
} }

View File

@@ -287,7 +287,7 @@ public class CombinedWordlReporting {
} }
if(isProcessFlag()) if(isProcessFlag())
{ {
setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail sent successfully.\n" : "")); setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail delivery queued.\n" : ""));
} }
} }
private void DesignReport(String [] reportData,String [][] reportlayout) private void DesignReport(String [] reportData,String [][] reportlayout)
@@ -488,16 +488,12 @@ public class CombinedWordlReporting {
{ {
if(isAutoemail) if(isAutoemail)
{ {
SendReports SR=new SendReports(); if (!ReportEmailJobExecutor.submitBatch(getErrCode(), processedOn, BranchId, getUserId(), runningMode))
SR.setErrCode(getErrCode()); {
SR.setProcessFlag(true); setErrMsg(getErrCode()+"SEML:warning:Bank Reports generated successfully, but the email queue is full. Please retry from the mailbox.");
SR.SendReportsByBatch(processedOn, BranchId,getUserId(),runningMode); setErrDesc("The bounded report email queue has reached its configured capacity.");
if (!SR.isProcessFlag()) setProcessFlag(false);
{ }
setErrMsg(SR.getErrMsg().replace(getErrCode()+"SEML:warning:", getErrCode()+"SEML:warning:Bank Reports generated successfully.\n\n"));
setErrDesc(SR.getErrDesc());
setProcessFlag(SR.isProcessFlag());
}
} }
} }
} }

View File

@@ -287,7 +287,7 @@ public class CombinedWordlReportingTabular {
} }
if(isProcessFlag()) if(isProcessFlag())
{ {
setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail sent successfully.\n" : "")); setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail delivery queued.\n" : ""));
} }
} }
private void DesignReport(String [] reportData,String [][] reportlayout) private void DesignReport(String [] reportData,String [][] reportlayout)
@@ -488,16 +488,12 @@ public class CombinedWordlReportingTabular {
{ {
if(isAutoemail) if(isAutoemail)
{ {
SendReports SR=new SendReports(); if (!ReportEmailJobExecutor.submitBatch(getErrCode(), processedOn, BranchId, getUserId(), runningMode))
SR.setErrCode(getErrCode()); {
SR.setProcessFlag(true); setErrMsg(getErrCode()+"SEML:warning:Bank Reports generated successfully, but the email queue is full. Please retry from the mailbox.");
SR.SendReportsByBatch(processedOn, BranchId,getUserId(),runningMode); setErrDesc("The bounded report email queue has reached its configured capacity.");
if (!SR.isProcessFlag()) setProcessFlag(false);
{ }
setErrMsg(SR.getErrMsg().replace(getErrCode()+"SEML:warning:", getErrCode()+"SEML:warning:Bank Reports generated successfully.\n\n"));
setErrDesc(SR.getErrDesc());
setProcessFlag(SR.isProcessFlag());
}
} }
} }
} }

View File

@@ -155,7 +155,7 @@ public class PartWiseReporting {
} }
if(isProcessFlag()) if(isProcessFlag())
{ {
setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail sent successfully.\n" : "")); setErrMsg(getErrCode()+"REPGEN:Info:Reports generated successfully."+(RS.isAutoemail() ? "\n\nEmail delivery queued.\n" : ""));
} }
} }
private boolean CreateReport(String [][] reportData,String [][] reportlayout,String reportType,String rootPath,String curDate,String processedOn,String reportBy,String repType,String UserId,String BranchId,boolean isArchive,String runningMode,String portfolioid) private boolean CreateReport(String [][] reportData,String [][] reportlayout,String reportType,String rootPath,String curDate,String processedOn,String reportBy,String repType,String UserId,String BranchId,boolean isArchive,String runningMode,String portfolioid)
@@ -405,16 +405,12 @@ public class PartWiseReporting {
{ {
if(isAutoemail) if(isAutoemail)
{ {
SendReports SR=new SendReports(); if (!ReportEmailJobExecutor.submitBatch(getErrCode(), processedOn, BranchId, UserId, runningMode))
SR.setErrCode(getErrCode()); {
SR.setProcessFlag(true); setErrMsg(getErrCode()+"SEML:warning:Bank Reports generated successfully, but the email queue is full. Please retry from the mailbox.");
SR.SendReportsByBatch(processedOn, BranchId, UserId,runningMode); setErrDesc("The bounded report email queue has reached its configured capacity.");
if (!SR.isProcessFlag()) setProcessFlag(false);
{ }
setErrMsg(SR.getErrMsg().replace(getErrCode()+"SEML:warning:", getErrCode()+"SEML:warning:Bank Reports generated successfully.\n\n"));
setErrDesc(SR.getErrDesc());
setProcessFlag(SR.isProcessFlag());
}
} }
} }
} }

View File

@@ -0,0 +1,82 @@
package matrix.nimble.operation.output.model;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/** Bounded background executor for automatically generated report-email batches. */
public final class ReportEmailJobExecutor {
private static final int WORKERS = integerSetting("matrix.mail.workers", 2);
private static final int QUEUE_CAPACITY = integerSetting("matrix.mail.queueCapacity", 50);
private static final AtomicInteger THREAD_SEQUENCE = new AtomicInteger();
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
WORKERS,
WORKERS,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new ReportEmailThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
private ReportEmailJobExecutor() {
}
public static boolean submitBatch(String errorCode, String batch, String branchId,
String userId, String runningMode) {
try {
EXECUTOR.execute(() -> sendBatch(errorCode, batch, branchId, userId, runningMode));
return true;
} catch (RejectedExecutionException rejected) {
System.err.println("Report email queue is full; batch was not queued: " + batch);
return false;
}
}
private static void sendBatch(String errorCode, String batch, String branchId,
String userId, String runningMode) {
try {
SendReports reports = new SendReports();
reports.setErrCode(errorCode);
reports.setProcessFlag(true);
reports.SendReportsByBatch(batch, branchId, userId, runningMode);
if (!reports.isProcessFlag()) {
System.err.println("Report email batch completed with failures: " + reports.getErrMsg());
}
} catch (RuntimeException exception) {
System.err.println("Report email batch failed unexpectedly: " + exception.getMessage());
exception.printStackTrace(System.err);
}
}
public static void shutdown() {
EXECUTOR.shutdown();
try {
if (!EXECUTOR.awaitTermination(10, TimeUnit.SECONDS)) {
EXECUTOR.shutdownNow();
}
} catch (InterruptedException interrupted) {
EXECUTOR.shutdownNow();
Thread.currentThread().interrupt();
}
}
private static int integerSetting(String name, int defaultValue) {
int value = Integer.parseInt(System.getProperty(name, Integer.toString(defaultValue)));
if (value < 1) {
throw new IllegalArgumentException(name + " must be greater than zero");
}
return value;
}
private static final class ReportEmailThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable task) {
Thread thread = new Thread(task, "cygnus-report-email-" + THREAD_SEQUENCE.incrementAndGet());
thread.setDaemon(true);
return thread;
}
}
}

View File

@@ -137,9 +137,7 @@ public class DBFunctions {
} }
private void ConnectDB() { private void ConnectDB() {
try { try {
Class.forName(ModFunc.decrypt(FFunc.getDBDriver())).newInstance(); con = DatabaseConnectionPool.getConnection();
//con = DriverManager.getConnection(ModFunc.decrypt(FFunc.getConnString()), ModFunc.decrypt(FFunc.getDBUser()), ModFunc.decrypt(FFunc.getDBPassword()));
con = DriverManager.getConnection("jdbc:postgresql://192.168.0.111:5432/matrix", "postgres", "M@triXPostgr3s@6202");
} catch (Exception exce) { } catch (Exception exce) {
setErrMsg(getErrCode()+"CONDB:Error:Could not connect with database. Please check the network connection."); setErrMsg(getErrCode()+"CONDB:Error:Could not connect with database. Please check the network connection.");
setErrDesc(exce.getMessage()); setErrDesc(exce.getMessage());
@@ -474,16 +472,16 @@ public class DBFunctions {
String [][] dataArray=null; String [][] dataArray=null;
Hashtable<String , String> dataHash=new Hashtable<String, String>(); Hashtable<String , String> dataHash=new Hashtable<String, String>();
int ColCount=0; int ColCount=0;
String Result=""; StringBuilder result = new StringBuilder();
int RowIndex=0; int RowIndex=0;
String tempStr=""; String tempStr="";
String hashKey=""; String hashKey="";
String hashValue=""; StringBuilder hashValue = new StringBuilder();
int hascol=getHashKeyColIndex(); int hascol=getHashKeyColIndex();
boolean ishash=isGenerateHash(); boolean ishash=isGenerateHash();
while (RstSet.next()) while (RstSet.next())
{ {
hashValue=""; hashValue.setLength(0);
if(RowIndex==0) if(RowIndex==0)
{ {
rsmd = RstSet.getMetaData(); rsmd = RstSet.getMetaData();
@@ -499,21 +497,21 @@ public class DBFunctions {
{ {
hashKey=tempStr; hashKey=tempStr;
} }
hashValue+=tempStr+ColDelimeter; hashValue.append(tempStr).append(ColDelimeter);
} }
Result+=tempStr+ColDelimeter; result.append(tempStr).append(ColDelimeter);
dataArray[RowIndex][i-1]=tempStr; dataArray[RowIndex][i-1]=tempStr;
} }
if(ishash) if(ishash)
{ {
dataHash.put(hashKey, hashValue); dataHash.put(hashKey, hashValue.toString());
} }
Result+=RowDelimeter; result.append(RowDelimeter);
RowIndex++; RowIndex++;
} }
setResultHash(dataHash); setResultHash(dataHash);
setResultArray(dataArray); setResultArray(dataArray);
return Result; return result.toString();
} }
private static int getRowCount(ResultSet set) throws SQLException private static int getRowCount(ResultSet set) throws SQLException
{ {
@@ -526,7 +524,7 @@ public class DBFunctions {
set.absolute(currentRow); // Restore it set.absolute(currentRow); // Restore it
return rowCount; return rowCount;
} }
public String CallProcedure(String ProcedureName,String ParameterList) public String CallProcedure(String ProcedureName,String ParameterList)
{ {
String Result=""; String Result="";
InitConfiguration(); InitConfiguration();
@@ -535,11 +533,10 @@ public class DBFunctions {
ConnectDB(); ConnectDB();
if(isProcessFlag()) if(isProcessFlag())
{ {
try try (CallableStatement proc = con.prepareCall("{ ? = call "+ProcedureName+" }"))
{ {
con.setAutoCommit(false); con.setAutoCommit(false);
CallableStatement proc =con.prepareCall("{ ? = call "+ProcedureName+" }"); proc.registerOutParameter(1, Types.LONGVARCHAR);
proc.registerOutParameter(1, Types.LONGVARCHAR);
if(ParameterList.isEmpty()){} if(ParameterList.isEmpty()){}
else else
{ {
@@ -553,13 +550,26 @@ public class DBFunctions {
Result = proc.getString(1); Result = proc.getString(1);
con.commit(); con.commit();
} }
catch(Exception excp) catch(Exception excp)
{ {
setProcessFlag(false); try {
setErrMsg(getErrCode()+"CLPROC:Error:Record updation failed. Please try again later"); if (con != null) {
setErrDesc(excp.getMessage()); con.rollback();
Result=getErrMsg(); }
} } catch (SQLException rollbackException) {
setErrDesc(excp.getMessage()+"; rollback failed: "+rollbackException.getMessage());
}
setProcessFlag(false);
setErrMsg(getErrCode()+"CLPROC:Error:Record updation failed. Please try again later");
if (getErrDesc() == null || !getErrDesc().contains("rollback failed")) {
setErrDesc(excp.getMessage());
}
Result=getErrMsg();
}
finally
{
CloseConnection();
}
} }
} }
return Result; return Result;

View File

@@ -0,0 +1,70 @@
package matrix.nimble.utilities;
import java.sql.Connection;
import java.sql.SQLException;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
/** Shared, bounded JDBC connection pool for the legacy database layer. */
public final class DatabaseConnectionPool {
private static final String DEFAULT_JDBC_URL = "jdbc:postgresql://192.168.0.111:5432/matrix";
private static final String DEFAULT_USERNAME = "postgres";
private static final String DEFAULT_PASSWORD = "M@triXPostgr3s@6202";
private static volatile HikariDataSource dataSource;
private DatabaseConnectionPool() {
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
private static HikariDataSource getDataSource() {
HikariDataSource current = dataSource;
if (current != null) {
return current;
}
synchronized (DatabaseConnectionPool.class) {
if (dataSource == null) {
HikariConfig config = new HikariConfig();
config.setPoolName("CygnusDatabasePool");
config.setJdbcUrl(setting("matrix.db.url", "MATRIX_DB_URL", DEFAULT_JDBC_URL));
config.setUsername(setting("matrix.db.username", "MATRIX_DB_USERNAME", DEFAULT_USERNAME));
config.setPassword(setting("matrix.db.password", "MATRIX_DB_PASSWORD", DEFAULT_PASSWORD));
config.setMaximumPoolSize(integerSetting("matrix.db.maximumPoolSize", 10));
config.setMinimumIdle(integerSetting("matrix.db.minimumIdle", 2));
config.setConnectionTimeout(longSetting("matrix.db.connectionTimeoutMs", 30_000L));
config.setValidationTimeout(longSetting("matrix.db.validationTimeoutMs", 5_000L));
config.setIdleTimeout(longSetting("matrix.db.idleTimeoutMs", 600_000L));
config.setMaxLifetime(longSetting("matrix.db.maxLifetimeMs", 1_800_000L));
config.setLeakDetectionThreshold(longSetting("matrix.db.leakDetectionThresholdMs", 60_000L));
dataSource = new HikariDataSource(config);
}
return dataSource;
}
}
public static synchronized void close() {
if (dataSource != null) {
dataSource.close();
dataSource = null;
}
}
private static String setting(String systemProperty, String environmentVariable, String defaultValue) {
String value = System.getProperty(systemProperty);
if (value == null || value.isBlank()) {
value = System.getenv(environmentVariable);
}
return value == null || value.isBlank() ? defaultValue : value;
}
private static int integerSetting(String name, int defaultValue) {
return Integer.parseInt(System.getProperty(name, Integer.toString(defaultValue)));
}
private static long longSetting(String name, long defaultValue) {
return Long.parseLong(System.getProperty(name, Long.toString(defaultValue)));
}
}

View File

@@ -0,0 +1,14 @@
package matrix.nimble.utilities;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import matrix.nimble.operation.output.model.ReportEmailJobExecutor;
/** Releases pooled database resources when Tomcat stops or reloads the application. */
public class DatabasePoolLifecycleListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent event) {
ReportEmailJobExecutor.shutdown();
DatabaseConnectionPool.close();
}
}

View File

@@ -4,13 +4,9 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Properties;
import java.util.Properties;
import java.util.Random;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@@ -29,9 +25,7 @@ import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeMultipart;
import com.fasterxml.jackson.databind.ObjectMapper; public class EmailFunctions {
public class EmailFunctions {
private String username; private String username;
private String password; private String password;
@@ -207,8 +201,11 @@ public class EmailFunctions {
this.configfile = "matrix.ini"; this.configfile = "matrix.ini";
this.errmsg = ""; this.errmsg = "";
Properties props=new Properties(); Properties props=new Properties();
props.setProperty("mail.smtp.auth", "true"); props.setProperty("mail.smtp.auth", "true");
props.setProperty("mail.smtp.starttls.enable", "true"); props.setProperty("mail.smtp.starttls.enable", "true");
props.setProperty("mail.smtp.connectiontimeout", System.getProperty("matrix.mail.connectionTimeoutMs", "30000"));
props.setProperty("mail.smtp.timeout", System.getProperty("matrix.mail.readTimeoutMs", "30000"));
props.setProperty("mail.smtp.writetimeout", System.getProperty("matrix.mail.writeTimeoutMs", "30000"));
if(getUsername().toLowerCase().contains("@arveebizpro.com")) { if(getUsername().toLowerCase().contains("@arveebizpro.com")) {
@@ -272,12 +269,9 @@ public class EmailFunctions {
} }
} }
); );
ObjectMapper Obj = new ObjectMapper(); try
String sessionStr = ""; {
try Message message = new MimeMessage(session);
{
sessionStr = Obj.writeValueAsString(session);
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(getFrom())); message.setFrom(new InternetAddress(getFrom()));
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(getTo())); message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(getTo()));
if(getCc().length() > 0) if(getCc().length() > 0)
@@ -309,25 +303,18 @@ public class EmailFunctions {
} }
// Send the complete message parts // Send the complete message parts
message.setContent(multipart); message.setContent(multipart);
if(CheckInternetConnection("www.google.co.in")) Transport.send(message);
{
Transport.send(message);
}
else
{
return "Error - Unable to access internet. Please check your internet connection";
}
/*if(isArchive()) /*if(isArchive())
{ {
new File(FileList.get(0)).delete(); new File(FileList.get(0)).delete();
}*/ }*/
}catch(IOException iexce) }catch(IOException iexce)
{ {
return "Error - Request Sent to : "+sessionStr + ", Response IO: " + iexce.getMessage(); return "Error - Unable to prepare email attachments: " + iexce.getMessage();
} }
catch(MessagingException exce) catch(MessagingException exce)
{ {
return "Error - Request Sent to : "+sessionStr + ", Response Message: " + exce.getMessage(); return "Error - Unable to send email: " + exce.getMessage();
} }
} }
else else
@@ -402,19 +389,4 @@ public class EmailFunctions {
FileList=new ArrayList<String>(); FileList=new ArrayList<String>();
FileList.add(zipFile); FileList.add(zipFile);
} }
public boolean CheckInternetConnection(String UrlToCheck) }
{
try
{
SocketChannel channel;
InetSocketAddress socketAddress = new InetSocketAddress(UrlToCheck, 80);
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(socketAddress);
return true;
}catch(Exception exce)
{
return false;
}
}
}

View File

@@ -3,9 +3,16 @@ package matrix.nimble.utilities;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class FileFunctions { public class FileFunctions {
private static final Object QUERY_CACHE_LOCK = new Object();
private static volatile Map<Integer, String> queryCache = Collections.emptyMap();
private static volatile long queryCacheLastModified = Long.MIN_VALUE;
private static volatile String queryCachePath = "";
private String ConnString; private String ConnString;
private String DBDriver; private String DBDriver;
private String DBUser; private String DBUser;
@@ -199,7 +206,7 @@ public class FileFunctions {
return "File deleted."; return "File deleted.";
} }
} }
public String GetQuery(int QueryIndex) throws IOException { public String GetQuery(int QueryIndex) throws IOException {
String realPath = (FileFunctions.class String realPath = (FileFunctions.class
.getResource("FileFunctions.class")).toString() .getResource("FileFunctions.class")).toString()
.replace("utilities/FileFunctions.class", "conf/") .replace("utilities/FileFunctions.class", "conf/")
@@ -212,15 +219,41 @@ public class FileFunctions {
{ {
realPath=realPath.replace("%20", " "); realPath=realPath.replace("%20", " ");
} }
File QueryFile = new File(realPath + "nimble.qry"); File queryFile = new File(realPath + "nimble.qry");
BufferedReader Breader = new BufferedReader(new FileReader(QueryFile)); refreshQueryCacheIfRequired(queryFile);
String TempContent; return queryCache.get(QueryIndex);
while ((TempContent = Breader.readLine()) != null) { }
if (TempContent.split(GlobalClass.ColDelim)[0].equals("Query"+QueryIndex)) {
return TempContent.replace("Query"+QueryIndex+GlobalClass.ColDelim, ""); private static void refreshQueryCacheIfRequired(File queryFile) throws IOException {
} String absolutePath = queryFile.getAbsolutePath();
} long lastModified = queryFile.lastModified();
Breader.close(); if (absolutePath.equals(queryCachePath) && lastModified == queryCacheLastModified) {
return TempContent; return;
} }
}
synchronized (QUERY_CACHE_LOCK) {
if (absolutePath.equals(queryCachePath) && lastModified == queryCacheLastModified) {
return;
}
Map<Integer, String> loadedQueries = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader(queryFile))) {
String line;
while ((line = reader.readLine()) != null) {
int delimiterIndex = line.indexOf(GlobalClass.ColDelim);
if (delimiterIndex <= 5 || !line.startsWith("Query")) {
continue;
}
try {
int queryIndex = Integer.parseInt(line.substring(5, delimiterIndex));
loadedQueries.put(queryIndex, line.substring(delimiterIndex + GlobalClass.ColDelim.length()));
} catch (NumberFormatException ignored) {
// Ignore malformed/non-query lines, matching the legacy lookup behavior.
}
}
}
queryCache = Collections.unmodifiableMap(loadedQueries);
queryCachePath = absolutePath;
queryCacheLastModified = lastModified;
}
}
}

View File

@@ -0,0 +1,20 @@
package matrix.nimble;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import matrix.nimble.utilities.FileFunctions;
class FileFunctionsQueryCacheTest {
@Test
void loadsQueriesFromNimbleQueryFileAndHandlesMissingCodes() throws Exception {
FileFunctions files = new FileFunctions("TEST");
String query = files.GetQuery(1);
assertTrue(query.startsWith("select!C0L!select u.user_id"));
assertNull(files.GetQuery(Integer.MAX_VALUE));
}
}