@@ -0,0 +1,20 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<classpath> | |||||
<classpathentry kind="src" output="bin/main" path="src/main/java"> | |||||
<attributes> | |||||
<attribute name="gradle_scope" value="main"/> | |||||
<attribute name="gradle_used_by_scope" value="main,test"/> | |||||
</attributes> | |||||
</classpathentry> | |||||
<classpathentry kind="src" output="bin/test" path="src/test/java"> | |||||
<attributes> | |||||
<attribute name="gradle_scope" value="test"/> | |||||
<attribute name="gradle_used_by_scope" value="test"/> | |||||
<attribute name="test" value="true"/> | |||||
</attributes> | |||||
</classpathentry> | |||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/> | |||||
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/> | |||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/> | |||||
<classpathentry kind="output" path="bin/default"/> | |||||
</classpath> |
@@ -0,0 +1,2 @@ | |||||
#Wed May 25 11:46:18 CST 2022 | |||||
gradle.version=5.6.3 |
@@ -0,0 +1,47 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<projectDescription> | |||||
<name>TBMS</name> | |||||
<comment></comment> | |||||
<projects> | |||||
</projects> | |||||
<buildSpec> | |||||
<buildCommand> | |||||
<name>org.eclipse.jdt.core.javabuilder</name> | |||||
<arguments> | |||||
</arguments> | |||||
</buildCommand> | |||||
<buildCommand> | |||||
<name>org.eclipse.wst.common.project.facet.core.builder</name> | |||||
<arguments> | |||||
</arguments> | |||||
</buildCommand> | |||||
<buildCommand> | |||||
<name>org.eclipse.wst.validation.validationbuilder</name> | |||||
<arguments> | |||||
</arguments> | |||||
</buildCommand> | |||||
<buildCommand> | |||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name> | |||||
<arguments> | |||||
</arguments> | |||||
</buildCommand> | |||||
</buildSpec> | |||||
<natures> | |||||
<nature>org.eclipse.jdt.core.javanature</nature> | |||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature> | |||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature> | |||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature> | |||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature> | |||||
</natures> | |||||
<filteredResources> | |||||
<filter> | |||||
<id>0</id> | |||||
<name></name> | |||||
<type>30</type> | |||||
<matcher> | |||||
<id>org.eclipse.core.resources.regexFilterMatcher</id> | |||||
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments> | |||||
</matcher> | |||||
</filter> | |||||
</filteredResources> | |||||
</projectDescription> |
@@ -0,0 +1,8 @@ | |||||
@echo off | |||||
copy /Y src\main\webapp\WEB-INF\classes\log4j.properties.prod src\main\webapp\WEB-INF\classes\log4j.properties | |||||
copy /Y src\main\webapp\WEB-INF\app.properties.local src\main\webapp\WEB-INF\app.properties | |||||
copy /Y src\main\webapp\META-INF\context.xml.2fi src\main\webapp\META-INF\context.xml | |||||
call a-clean-undeploy | |||||
call gradlew clean war | |||||
copy build\libs\%PROJECT_NAME%.war "%TC_BASE%\webapps" | |||||
call on |
@@ -0,0 +1,8 @@ | |||||
@echo off | |||||
copy /Y src\main\webapp\WEB-INF\classes\log4j.properties.prod src\main\webapp\WEB-INF\classes\log4j.properties | |||||
copy /Y src\main\webapp\WEB-INF\app.properties.local src\main\webapp\WEB-INF\app.properties | |||||
copy /Y src\main\webapp\META-INF\context.xml.local src\main\webapp\META-INF\context.xml | |||||
call a-clean-undeploy | |||||
call gradlew clean war | |||||
copy build\libs\%PROJECT_NAME%.war "%TC_BASE%\webapps" | |||||
call on |
@@ -0,0 +1,6 @@ | |||||
@echo off | |||||
call setenv | |||||
call off | |||||
del "%TC_BASE%\webapps\%PROJECT_NAME%.war" | |||||
rd /s /q "%TC_BASE%\webapps\%PROJECT_NAME%" | |||||
rd /s /q "%TC_BASE%\work\Catalina\localhost\%PROJECT_NAME%" |
@@ -0,0 +1,5 @@ | |||||
@echo off | |||||
copy /Y src\main\webapp\WEB-INF\classes\log4j.properties.prod src\main\webapp\WEB-INF\classes\log4j.properties | |||||
copy /Y src\main\webapp\WEB-INF\app.properties.prod src\main\webapp\WEB-INF\app.properties | |||||
copy /Y src\main\webapp\META-INF\context.xml.2fi src\main\webapp\META-INF\context.xml | |||||
call gradlew clean war |
@@ -0,0 +1,89 @@ | |||||
apply plugin: 'java' | |||||
apply plugin: 'eclipse' | |||||
apply plugin: 'war' | |||||
sourceCompatibility = 11 | |||||
targetCompatibility = 11 | |||||
tasks.withType(JavaCompile) { | |||||
options.encoding = 'UTF-8' | |||||
} | |||||
repositories { | |||||
jcenter() | |||||
mavenCentral() | |||||
maven { | |||||
url 'http://jaspersoft.jfrog.io/jaspersoft/third-party-ce-artifacts/' | |||||
} | |||||
} | |||||
war { | |||||
archiveName = 'tbms.war' | |||||
classpath = classpath - sourceSets.main.output | |||||
from (jar) { | |||||
into 'WEB-INF/lib' | |||||
} | |||||
} | |||||
dependencies { | |||||
// for JDK 11 | |||||
compile 'javax.xml.bind:jaxb-api:2.3.1' | |||||
compile 'javax.annotation:javax.annotation-api:1.3.2' | |||||
compile 'com.sun.activation:javax.activation:1.2.0' | |||||
compile 'org.springframework:spring-context:4.3.14.RELEASE' | |||||
compile 'org.springframework:spring-context-support:4.3.14.RELEASE' | |||||
compile 'org.springframework:spring-orm:4.3.14.RELEASE' | |||||
compile 'org.springframework:spring-webmvc:4.3.14.RELEASE' | |||||
compile 'org.springframework.security:spring-security-config:4.2.3.RELEASE' | |||||
compile 'org.springframework.security:spring-security-web:4.2.3.RELEASE' | |||||
compile 'org.apache.commons:commons-lang3:3.5' | |||||
compile 'commons-fileupload:commons-fileupload:1.3.2' | |||||
compile 'org.apache.poi:poi:3.17' | |||||
compile 'org.apache.poi:poi-ooxml:3.17' | |||||
compile 'org.freemarker:freemarker:2.3.23' | |||||
compile 'org.hibernate:hibernate-core:5.2.12.Final' | |||||
compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final' | |||||
compile 'com.fasterxml.jackson.core:jackson-core:2.8.8' | |||||
compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.8' | |||||
compile 'com.fasterxml.jackson.core:jackson-databind:2.8.8.1' | |||||
compile 'org.imgscalr:imgscalr-lib:4.2' | |||||
compile(group: 'net.sf.jasperreports', name: 'jasperreports', version:'6.5.1') { | |||||
exclude(module: 'bcmail-jdk14') | |||||
exclude(module: 'bcprov-jdk14') | |||||
exclude(module: 'bctsp-jdk14') | |||||
exclude(module: 'castor-xml') | |||||
exclude(module: 'jackson-core') | |||||
exclude(module: 'jackson-annotations') | |||||
exclude(module: 'jackson-databind') | |||||
exclude(module: 'lucene-core') | |||||
exclude(module: 'lucene-analyzers-common') | |||||
exclude(module: 'lucene-queryparser') | |||||
exclude(module: 'olap4j') | |||||
} | |||||
//providedCompile 'org.slf4j:slf4j-api:1.7.25' | |||||
//providedCompile 'org.slf4j:slf4j-log4j12:1.7.25' | |||||
providedCompile 'javax.servlet:javax.servlet-api:3.1.0' | |||||
providedCompile 'mysql:mysql-connector-java:8.0.15' | |||||
//testCompile 'junit:junit:4.12' | |||||
compile 'com.google.zxing:javase:3.2.1' | |||||
compile 'javax.mail:mail:1.4.7' | |||||
compile group: 'org.liquibase', name: 'liquibase-core', version: '3.5.3' | |||||
fileTree(dir: 'lib', include: '*.jar') | |||||
} |
@@ -0,0 +1,295 @@ | |||||
-- anna 20/03/2020 | |||||
-- | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MEETING_MAINTAIN', 'Meeting', 'Maintain Meeting'); | |||||
INSERT INTO `tbmsdb`.`users_authorities` (`userId`, `authority`) VALUES ('1', 'MEETING_MAINTAIN'); | |||||
ALTER TABLE `tbmsdb`.`meeting` CHANGE COLUMN `datetime` `date` DATE NOT NULL ; | |||||
-- anna 23/03/2020 | |||||
-- | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('ORDER_MAINTAIN', 'order', 'Maintain order'); | |||||
UPDATE `tbmsdb`.`authorities` SET `module`='customer' WHERE `authority`='CUSTOMER_MAINTAIN'; | |||||
UPDATE `tbmsdb`.`authorities` SET `module`='meeting' WHERE `authority`='MEETING_MAINTAIN'; | |||||
INSERT INTO `tbmsdb`.`users_authorities` (`userId`, `authority`) VALUES ('1', 'ORDER_MAINTAIN'); | |||||
ALTER TABLE `tbmsdb`.`order` RENAME TO `tbmsdb`.`orders` ; | |||||
-- anna 31/03/2020 | |||||
-- | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD COLUMN `refNo` VARCHAR(100) NULL DEFAULT NULL AFTER `ref`; | |||||
-- anna 07/04/2020 | |||||
-- | |||||
ALTER TABLE `tbmsdb`.`files` | |||||
ADD COLUMN `skey` VARCHAR(16) NOT NULL AFTER `description`; | |||||
-- anna 09/04/2020 | |||||
-- | |||||
ALTER TABLE `tbmsdb`.`customer` | |||||
ADD COLUMN `companyName` VARCHAR(255) NULL AFTER `phone2`; | |||||
-- anna 14/04/2020 | |||||
-- | |||||
ALTER TABLE `tbmsdb`.`customer` | |||||
CHANGE COLUMN `surname` `surname` VARCHAR(255) NULL DEFAULT NULL , | |||||
CHANGE COLUMN `firstName` `firstName` VARCHAR(255) NULL DEFAULT NULL ; | |||||
-- anna 11/05/2020 | |||||
-- | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD INDEX `cust_order` (`id` ASC, `custId` ASC); | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD INDEX `order_ref` (`id` ASC, `ref` ASC); | |||||
ALTER TABLE `tbmsdb`.`customer` | |||||
ADD INDEX `cust_ref` (`id` ASC, `ref` ASC); | |||||
-- anna 15/05/2020 | |||||
-- | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`, `description`) VALUES ('AMOUNT_REMIND', 'order', 'Order Amount Remind', NULL); | |||||
DROP TABLE IF EXISTS `audit_log`; | |||||
CREATE TABLE `audit_log` ( | |||||
`tableName` varchar(30) NOT NULL, | |||||
`recordId` int(11) NOT NULL, | |||||
`modifiedBy` int(11) DEFAULT NULL, | |||||
`modified` datetime DEFAULT NULL, | |||||
`oldData` json DEFAULT NULL, | |||||
`newData` json DEFAULT NULL | |||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |||||
-- anna 28/05/2020 | |||||
ALTER TABLE `tbmsdb`.`meeting` | |||||
CHANGE COLUMN `date` `date` DATETIME NOT NULL ; | |||||
-- Matthew | |||||
-- 09/06/2020 | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('ORDER_CANCEL', 'order', 'Order Cancel'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MEETING_DELETE', 'meeting', 'Meeting Delete'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('CUSTOMER_DELETE', 'customer', 'Customer Delete'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('FILE_DELETE', 'file', 'File Delete'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('PAYMENT_REPORT', 'report', 'Payment Report Auth'); | |||||
-- Jason Lam | |||||
-- 10/06/2020 | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD COLUMN `outDate` DATE NULL AFTER `status`, | |||||
ADD COLUMN `outRemark` VARCHAR(255) NULL AFTER `outDate`, | |||||
ADD COLUMN `inDate` DATE NULL AFTER `outRemark`, | |||||
ADD COLUMN `inRemark` VARCHAR(255) NULL AFTER `inDate`; | |||||
--Jason Lam | |||||
--11/06/2020 | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD COLUMN `ignore` TINYINT(1) '0' AFTER `inRemark`; | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
CHANGE COLUMN `ignore` `ignoreC` TINYINT(1) NULL DEFAULT '0' ; | |||||
-- anna | |||||
-- 15/06/2020 | |||||
ALTER TABLE `tbmsdb`.`audit_log` | |||||
DROP COLUMN `oldData`, | |||||
CHANGE COLUMN `newData` `dataStr` JSON NULL DEFAULT NULL , | |||||
ADD COLUMN `actionStr` VARCHAR(255) NULL DEFAULT NULL AFTER `modified`, | |||||
ADD COLUMN `refId` INT(11) NULL DEFAULT NULL AFTER `dataStr`, | |||||
ADD COLUMN `refType` VARCHAR(255) NULL DEFAULT NULL AFTER `refId`; | |||||
-- anna | |||||
-- 23/06/2020 | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD COLUMN `workshopStatus` INT(11) NULL DEFAULT NULL AFTER `ignoreC`; | |||||
-- anna | |||||
-- 29/06/2020 | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('ORDER_ITEM_DELETE', 'order', 'Order Item Delete'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('PAYMENT_DELETE', 'order', 'Payment Delete'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('WORKSHOP_MAINTAIN', 'workshop', 'Maintain Workshop'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('CALENDAR_VIEW', 'calendar', 'View Calendar'); | |||||
-- anna | |||||
-- 13/07/2020 | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD COLUMN `targetCompleteDate` DATE NULL DEFAULT NULL AFTER `workshopStatus`; | |||||
-- anna | |||||
-- 21/07/2020 | |||||
ALTER TABLE `tbmsdb`.`customer` | |||||
ADD COLUMN `tag` VARCHAR(255) NULL DEFAULT NULL AFTER `ref`; | |||||
-- matthew | |||||
-- 07/09/2020 | |||||
INSERT INTO `tbmsdb`.`settings` (`name`, `value`) VALUES ('JS.version', '1'); | |||||
-- anna | |||||
-- 01/12/2020 | |||||
ALTER TABLE `tbmsdb`.`meeting` | |||||
ADD COLUMN `staffName` VARCHAR(45) NULL DEFAULT NULL AFTER `remarks`; | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD COLUMN `inStatus` INT(11) NULL DEFAULT NULL AFTER `targetCompleteDate`, | |||||
ADD COLUMN `outStatus` INT(11) NULL DEFAULT NULL AFTER `inStatus`; | |||||
-- anna | |||||
-- 10/06/2021 | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MATERIAL_MAINTAIN', 'price_list', 'Maintain Material'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('BAND_DELETE', 'price_list', 'Delete Band'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('VIEW_EXPECTED_PRICE', 'price_list', 'View Expected Price'); | |||||
-- anna | |||||
-- 29/06/2021 | |||||
INSERT INTO `tbmsdb`.`settings` (`name`, `value`) VALUES ('MAIL.temp.subject', '[INVOICE] Order No.: {$orderNo}'); | |||||
-- anna | |||||
-- 06/07/2021 | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('MATERIAL_MAIL_TEMPLATE', 'setup', 'Maintain Mail Template'); | |||||
INSERT INTO `tbmsdb`.`authorities` (`authority`, `module`, `name`) VALUES ('PRICE_LIST_VIEW', 'price_list', 'View Price List'); | |||||
UPDATE `tbmsdb`.`authorities` SET `authority`='PRICE_LIST_MAINTAIN', `name`='Maintain Price List' WHERE `authority`='MATERIAL_MAINTAIN'; | |||||
-- anna | |||||
-- 19/07/2021 | |||||
CREATE TABLE `material_band` ( | |||||
`id` int(11) NOT NULL AUTO_INCREMENT, | |||||
`versionId` mediumint(8) unsigned NOT NULL DEFAULT '0', | |||||
`deleted` tinyint(1) NOT NULL DEFAULT '0', | |||||
`createdBy` int(11) DEFAULT NULL, | |||||
`modifiedBy` int(11) DEFAULT NULL, | |||||
`created` datetime DEFAULT NULL, | |||||
`modified` datetime DEFAULT NULL, | |||||
`name` varchar(255) NOT NULL, | |||||
`fileId` int(11) DEFAULT NULL, | |||||
`expectedPriceFormula` varchar(255) DEFAULT NULL, | |||||
PRIMARY KEY (`id`) | |||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; | |||||
CREATE TABLE `material_item` ( | |||||
`id` int(11) NOT NULL AUTO_INCREMENT, | |||||
`versionId` mediumint(8) unsigned NOT NULL DEFAULT '0', | |||||
`deleted` tinyint(1) NOT NULL DEFAULT '0', | |||||
`createdBy` int(11) DEFAULT NULL, | |||||
`modifiedBy` int(11) DEFAULT NULL, | |||||
`created` datetime DEFAULT NULL, | |||||
`modified` datetime DEFAULT NULL, | |||||
`bandId` int(11) DEFAULT NULL, | |||||
`name` varchar(255) DEFAULT NULL, | |||||
`bunch` varchar(255) DEFAULT NULL, | |||||
`itemNo` varchar(255) DEFAULT NULL, | |||||
`cost` decimal(18,4) DEFAULT '0.0000', | |||||
`suitPrice` decimal(18,4) DEFAULT NULL, | |||||
`jacketPrice` decimal(18,4) DEFAULT NULL, | |||||
`overcoatPrice` decimal(18,4) DEFAULT NULL, | |||||
`pantsPrice` decimal(18,4) DEFAULT NULL, | |||||
`otherPrice` decimal(18,4) DEFAULT NULL, | |||||
PRIMARY KEY (`id`) | |||||
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8; | |||||
-- anna | |||||
-- 10/01/2022 | |||||
ALTER TABLE `tbmsdb`.`material_item` | |||||
ADD COLUMN `fabricNum` VARCHAR(255) NULL DEFAULT NULL AFTER `bunch`; | |||||
-- anna | |||||
-- 4/03/2022 | |||||
ALTER TABLE `tbmsdb`.`material_item` | |||||
ADD COLUMN `orderIdx` INT(11) NOT NULL DEFAULT 0 AFTER `otherPrice`; | |||||
CREATE TABLE `user_settings` ( | |||||
`userId` int(11) NOT NULL, | |||||
`name` varchar(255) NOT NULL, | |||||
`value` varchar(1000) DEFAULT NULL, | |||||
`category` varchar(50) DEFAULT NULL, | |||||
`type` varchar(45) DEFAULT NULL, | |||||
PRIMARY KEY (`userId`,`name`) | |||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |||||
-- anna | |||||
-- 22/03/2022 | |||||
CREATE TABLE `sys_groups` ( | |||||
`id` int(11) NOT NULL AUTO_INCREMENT, | |||||
`deleted` tinyint(1) NOT NULL, | |||||
`versionId` mediumint(8) unsigned NOT NULL, | |||||
`created` datetime DEFAULT NULL, | |||||
`createdBy` int(11) DEFAULT NULL, | |||||
`modified` datetime DEFAULT NULL, | |||||
`modifiedBy` int(11) DEFAULT NULL, | |||||
`name` varchar(50) NOT NULL, | |||||
`description` varchar(255) DEFAULT NULL, | |||||
PRIMARY KEY (`id`) | |||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; | |||||
ALTER TABLE `tbmsdb`.`groups` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`users` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`customer` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`files` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`files_blob` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`files_ref` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`material_band` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`material_item` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`meeting` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`order_item` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`orders` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`payment` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
ALTER TABLE `tbmsdb`.`message` | |||||
ADD COLUMN `sysGroupId` INT(11) NOT NULL DEFAULT 0 AFTER `id`; | |||||
-- anna | |||||
-- 18/05/2022 | |||||
ALTER TABLE `tbmsdb`.`sys_groups` | |||||
ADD COLUMN `mailBody` TEXT NULL DEFAULT NULL AFTER `description`, | |||||
ADD COLUMN `mailSubject` TEXT NULL DEFAULT NULL AFTER `mailBody`; | |||||
ALTER TABLE `tbmsdb`.`sys_groups` | |||||
ADD COLUMN `companyName` VARCHAR(255) NULL DEFAULT NULL AFTER `mailSubject`, | |||||
ADD COLUMN `phone` VARCHAR(255) NULL DEFAULT NULL AFTER `companyName`, | |||||
ADD COLUMN `fax` VARCHAR(255) NULL DEFAULT NULL AFTER `phone`, | |||||
ADD COLUMN `email` VARCHAR(255) NULL DEFAULT NULL AFTER `fax`, | |||||
ADD COLUMN `address` VARCHAR(500) NULL DEFAULT NULL AFTER `email`; | |||||
-- anna | |||||
-- 23/05/2022 | |||||
ALTER TABLE `tbmsdb`.`sys_groups` | |||||
ADD COLUMN `smtp_host` VARCHAR(255) NULL DEFAULT NULL AFTER `address`, | |||||
ADD COLUMN `smtp_port` VARCHAR(5) NULL DEFAULT NULL AFTER `smtp_host`, | |||||
ADD COLUMN `smtp_username` VARCHAR(255) NULL DEFAULT NULL AFTER `smtp_port`, | |||||
ADD COLUMN `smtp_password` VARCHAR(255) NULL DEFAULT NULL AFTER `smtp_username`; | |||||
@@ -0,0 +1,12 @@ | |||||
-- | |||||
-- ON stock_ledger INSERT: SET part.balance = stock_ledger.balance | |||||
-- | |||||
DROP TRIGGER IF EXISTS `tbmsdb`.`stock_ledger_AFTER_INSERT`; | |||||
DELIMITER $$ | |||||
USE `tbmsdb`$$ | |||||
CREATE DEFINER = `root`@`localhost` TRIGGER `tbmsdb`.`stock_ledger_AFTER_INSERT` AFTER INSERT ON `stock_ledger` FOR EACH ROW | |||||
BEGIN | |||||
UPDATE `part` SET `balance` = NEW.`balance` WHERE `id` = NEW.`partId`; | |||||
END$$ | |||||
DELIMITER ; |
@@ -0,0 +1,6 @@ | |||||
-- SET FOREIGN_KEY_CHECKS=0; | |||||
-- TRUNCATE `job`; | |||||
-- SET FOREIGN_KEY_CHECKS=1; |
@@ -0,0 +1,3 @@ | |||||
#!/bin/bash | |||||
source ./setenv.sh | |||||
cp -r src/main/webapp/resources/js/* ${tc_instance}/webapps/sms/resources/js/ |
@@ -0,0 +1,3 @@ | |||||
@ECHO OFF | |||||
rd /S /Q C:\Tomcat\webapps\tbms\resources | |||||
mklink /J C:\Tomcat\webapps\tbms\resources C:\workspace\TBMS\src\main\webapp\resources |
@@ -0,0 +1,4 @@ | |||||
#!/bin/bash | |||||
bash ./undeploy.sh | |||||
bash ./publish.sh | |||||
bash ./on.sh |
@@ -0,0 +1,47 @@ | |||||
Manage Master Data | |||||
Vendor | |||||
Equipment Type | |||||
Equipment | |||||
Equipment Part | |||||
Equipment and Parts relation | |||||
Manage Preventive Maintenance (PM) Plan | |||||
Setup Preventive Maintenance Plan | |||||
Equipment | |||||
Plan name and description (e.g. checklist) | |||||
Frequency (e.g. Daily, Weekly, Monthly, Yearly) | |||||
Resp. Team/Person | |||||
Preferred timeslot (or Shift) | |||||
Preferred day of week (e.g. Monday, Tuesday) | |||||
Time required to perform the PM | |||||
Support file attachments | |||||
Provide daily and upcoming pending PM plan (i.e. Pending to do list) | |||||
Display all the details above, plus Last Performed Date | |||||
Upon PM completion, maintenance staff will create Service Log to record, then PM plan is completed (by matching Service Log with corresponding PM plan) | |||||
Incident Log should be created if any issues found during PM | |||||
Manage Incident | |||||
Search Incident Logs | |||||
Create/Edit Incident Logs | |||||
Support file attachments | |||||
If part replacement is required, it's used as the base for Part Stock Out | |||||
Manage Problem | |||||
Search Problem Logs | |||||
Create/Edit Problem Logs | |||||
Support linking to related Incident Logs | |||||
Support file attachments | |||||
Manage Parts Inventory | |||||
Stock In/Out and Adjustment | |||||
Search Parts | |||||
Create/Edit Parts | |||||
Support re-order level, can filter list of parts that the balance is below re-order level | |||||
Support First-In-First-Out for Stock Out ??? | |||||
Parts I/O Ledger | |||||
Android App | |||||
View PM Plans | |||||
Input Service and Incident Logs | |||||
Receive Incident service requests | |||||
@@ -0,0 +1,5 @@ | |||||
distributionBase=GRADLE_USER_HOME | |||||
distributionPath=wrapper/dists | |||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip | |||||
zipStoreBase=GRADLE_USER_HOME | |||||
zipStorePath=wrapper/dists |
@@ -0,0 +1,188 @@ | |||||
#!/usr/bin/env sh | |||||
# | |||||
# Copyright 2015 the original author or authors. | |||||
# | |||||
# 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. | |||||
# | |||||
############################################################################## | |||||
## | |||||
## Gradle start up script for UN*X | |||||
## | |||||
############################################################################## | |||||
# Attempt to set APP_HOME | |||||
# Resolve links: $0 may be a link | |||||
PRG="$0" | |||||
# Need this for relative symlinks. | |||||
while [ -h "$PRG" ] ; do | |||||
ls=`ls -ld "$PRG"` | |||||
link=`expr "$ls" : '.*-> \(.*\)$'` | |||||
if expr "$link" : '/.*' > /dev/null; then | |||||
PRG="$link" | |||||
else | |||||
PRG=`dirname "$PRG"`"/$link" | |||||
fi | |||||
done | |||||
SAVED="`pwd`" | |||||
cd "`dirname \"$PRG\"`/" >/dev/null | |||||
APP_HOME="`pwd -P`" | |||||
cd "$SAVED" >/dev/null | |||||
APP_NAME="Gradle" | |||||
APP_BASE_NAME=`basename "$0"` | |||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | |||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | |||||
# Use the maximum available, or set MAX_FD != -1 to use that value. | |||||
MAX_FD="maximum" | |||||
warn () { | |||||
echo "$*" | |||||
} | |||||
die () { | |||||
echo | |||||
echo "$*" | |||||
echo | |||||
exit 1 | |||||
} | |||||
# OS specific support (must be 'true' or 'false'). | |||||
cygwin=false | |||||
msys=false | |||||
darwin=false | |||||
nonstop=false | |||||
case "`uname`" in | |||||
CYGWIN* ) | |||||
cygwin=true | |||||
;; | |||||
Darwin* ) | |||||
darwin=true | |||||
;; | |||||
MINGW* ) | |||||
msys=true | |||||
;; | |||||
NONSTOP* ) | |||||
nonstop=true | |||||
;; | |||||
esac | |||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | |||||
# Determine the Java command to use to start the JVM. | |||||
if [ -n "$JAVA_HOME" ] ; then | |||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | |||||
# IBM's JDK on AIX uses strange locations for the executables | |||||
JAVACMD="$JAVA_HOME/jre/sh/java" | |||||
else | |||||
JAVACMD="$JAVA_HOME/bin/java" | |||||
fi | |||||
if [ ! -x "$JAVACMD" ] ; then | |||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME | |||||
Please set the JAVA_HOME variable in your environment to match the | |||||
location of your Java installation." | |||||
fi | |||||
else | |||||
JAVACMD="java" | |||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | |||||
Please set the JAVA_HOME variable in your environment to match the | |||||
location of your Java installation." | |||||
fi | |||||
# Increase the maximum file descriptors if we can. | |||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then | |||||
MAX_FD_LIMIT=`ulimit -H -n` | |||||
if [ $? -eq 0 ] ; then | |||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then | |||||
MAX_FD="$MAX_FD_LIMIT" | |||||
fi | |||||
ulimit -n $MAX_FD | |||||
if [ $? -ne 0 ] ; then | |||||
warn "Could not set maximum file descriptor limit: $MAX_FD" | |||||
fi | |||||
else | |||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" | |||||
fi | |||||
fi | |||||
# For Darwin, add options to specify how the application appears in the dock | |||||
if $darwin; then | |||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" | |||||
fi | |||||
# For Cygwin, switch paths to Windows format before running java | |||||
if $cygwin ; then | |||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"` | |||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` | |||||
JAVACMD=`cygpath --unix "$JAVACMD"` | |||||
# We build the pattern for arguments to be converted via cygpath | |||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` | |||||
SEP="" | |||||
for dir in $ROOTDIRSRAW ; do | |||||
ROOTDIRS="$ROOTDIRS$SEP$dir" | |||||
SEP="|" | |||||
done | |||||
OURCYGPATTERN="(^($ROOTDIRS))" | |||||
# Add a user-defined pattern to the cygpath arguments | |||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then | |||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" | |||||
fi | |||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh | |||||
i=0 | |||||
for arg in "$@" ; do | |||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` | |||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option | |||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition | |||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` | |||||
else | |||||
eval `echo args$i`="\"$arg\"" | |||||
fi | |||||
i=$((i+1)) | |||||
done | |||||
case $i in | |||||
(0) set -- ;; | |||||
(1) set -- "$args0" ;; | |||||
(2) set -- "$args0" "$args1" ;; | |||||
(3) set -- "$args0" "$args1" "$args2" ;; | |||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; | |||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; | |||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; | |||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; | |||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; | |||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; | |||||
esac | |||||
fi | |||||
# Escape application args | |||||
save () { | |||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done | |||||
echo " " | |||||
} | |||||
APP_ARGS=$(save "$@") | |||||
# Collect all arguments for the java command, following the shell quoting and substitution rules | |||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" | |||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong | |||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then | |||||
cd "$(dirname "$0")" | |||||
fi | |||||
exec "$JAVACMD" "$@" |
@@ -0,0 +1,100 @@ | |||||
@rem | |||||
@rem Copyright 2015 the original author or authors. | |||||
@rem | |||||
@rem Licensed under the Apache License, Version 2.0 (the "License"); | |||||
@rem you may not use this file except in compliance with the License. | |||||
@rem You may obtain a copy of the License at | |||||
@rem | |||||
@rem http://www.apache.org/licenses/LICENSE-2.0 | |||||
@rem | |||||
@rem Unless required by applicable law or agreed to in writing, software | |||||
@rem distributed under the License is distributed on an "AS IS" BASIS, | |||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
@rem See the License for the specific language governing permissions and | |||||
@rem limitations under the License. | |||||
@rem | |||||
@if "%DEBUG%" == "" @echo off | |||||
@rem ########################################################################## | |||||
@rem | |||||
@rem Gradle startup script for Windows | |||||
@rem | |||||
@rem ########################################################################## | |||||
@rem Set local scope for the variables with windows NT shell | |||||
if "%OS%"=="Windows_NT" setlocal | |||||
set DIRNAME=%~dp0 | |||||
if "%DIRNAME%" == "" set DIRNAME=. | |||||
set APP_BASE_NAME=%~n0 | |||||
set APP_HOME=%DIRNAME% | |||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | |||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" | |||||
@rem Find java.exe | |||||
if defined JAVA_HOME goto findJavaFromJavaHome | |||||
set JAVA_EXE=java.exe | |||||
%JAVA_EXE% -version >NUL 2>&1 | |||||
if "%ERRORLEVEL%" == "0" goto init | |||||
echo. | |||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | |||||
echo. | |||||
echo Please set the JAVA_HOME variable in your environment to match the | |||||
echo location of your Java installation. | |||||
goto fail | |||||
:findJavaFromJavaHome | |||||
set JAVA_HOME=%JAVA_HOME:"=% | |||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe | |||||
if exist "%JAVA_EXE%" goto init | |||||
echo. | |||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% | |||||
echo. | |||||
echo Please set the JAVA_HOME variable in your environment to match the | |||||
echo location of your Java installation. | |||||
goto fail | |||||
:init | |||||
@rem Get command-line arguments, handling Windows variants | |||||
if not "%OS%" == "Windows_NT" goto win9xME_args | |||||
:win9xME_args | |||||
@rem Slurp the command line arguments. | |||||
set CMD_LINE_ARGS= | |||||
set _SKIP=2 | |||||
:win9xME_args_slurp | |||||
if "x%~1" == "x" goto execute | |||||
set CMD_LINE_ARGS=%* | |||||
:execute | |||||
@rem Setup the command line | |||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | |||||
@rem Execute Gradle | |||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% | |||||
:end | |||||
@rem End local scope for the variables with windows NT shell | |||||
if "%ERRORLEVEL%"=="0" goto mainEnd | |||||
:fail | |||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of | |||||
rem the _cmd.exe /c_ return code! | |||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 | |||||
exit /b 1 | |||||
:mainEnd | |||||
if "%OS%"=="Windows_NT" endlocal | |||||
:omega |
@@ -0,0 +1,3 @@ | |||||
@echo off | |||||
sc stop Tomcat8 | |||||
ping 127.0.0.1 > nul |
@@ -0,0 +1,3 @@ | |||||
#!/bin/bash | |||||
source ./setenv.sh | |||||
bash ${tc_instance}/bin/catalina.sh stop >> /dev/null 2>&1 |
@@ -0,0 +1,2 @@ | |||||
@echo off | |||||
sc start Tomcat8 |
@@ -0,0 +1,3 @@ | |||||
#!/bin/bash | |||||
source ./setenv.sh | |||||
bash ${tc_instance}/bin/catalina.sh start >> /dev/null 2>&1 |
@@ -0,0 +1,5 @@ | |||||
#!/bin/bash | |||||
cp src/main/webapp/WEB-INF/classes/log4j.properties.prod src/main/webapp/WEB-INF/classes/log4j.properties | |||||
cp src/main/webapp/WEB-INF/app.properties.prod src/main/webapp/WEB-INF/app.properties | |||||
rm -rf src/main/webapp/META-INF/context.xml | |||||
bash ./gradlew clean war |
@@ -0,0 +1,12 @@ | |||||
#!/bin/bash | |||||
source ./setenv.sh | |||||
cp src/main/webapp/WEB-INF/classes/log4j.properties.local.mac src/main/webapp/WEB-INF/classes/log4j.properties | |||||
cp src/main/webapp/WEB-INF/app.properties.local.mac src/main/webapp/WEB-INF/app.properties | |||||
cp src/main/webapp/META-INF/context.xml.2fi src/main/webapp/META-INF/context.xml | |||||
#mvn clean package | |||||
#cp target/*.war ${tc_instance}/webapps/ | |||||
bash ./gradlew clean war | |||||
cp build/libs/*.war ${tc_instance}/webapps/ |
@@ -0,0 +1,3 @@ | |||||
@echo off | |||||
set PROJECT_NAME=tbms | |||||
set TC_BASE=C:\Tomcat |
@@ -0,0 +1,2 @@ | |||||
project=tbms | |||||
tc_instance=/Users/2fiadmin/tomcat |
@@ -0,0 +1 @@ | |||||
rootProject.name = 'TBMS' |
@@ -0,0 +1,135 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core; | |||||
import java.io.Serializable; | |||||
import java.util.Date; | |||||
import javax.persistence.Column; | |||||
import javax.persistence.GeneratedValue; | |||||
import javax.persistence.GenerationType; | |||||
import javax.persistence.Id; | |||||
import javax.persistence.MappedSuperclass; | |||||
import javax.persistence.Version; | |||||
@MappedSuperclass | |||||
public abstract class BaseEntity<PK extends Serializable> implements Serializable, Cloneable { | |||||
private static final long serialVersionUID = -7211267047474252631L; | |||||
@Id | |||||
@GeneratedValue(strategy = GenerationType.IDENTITY) | |||||
private PK id; | |||||
@Column(columnDefinition = "int(11)") | |||||
private Integer sysGroupId; | |||||
@Version | |||||
@Column(columnDefinition = "mediumint unsigned", nullable = false) | |||||
private int versionId; // hibernate optimistic lock version id | |||||
@Column(columnDefinition = "boolean", nullable = false) | |||||
private boolean deleted; // defaults to false | |||||
@Column(updatable = false) | |||||
private Integer createdBy; // User ID that created this record | |||||
@Column | |||||
private Integer modifiedBy; // User ID that last modified this record | |||||
@Column(updatable = false, nullable = false) | |||||
private Date created; // date created | |||||
@Column(nullable = false) | |||||
private Date modified; // last modified date | |||||
/** Default constructor */ | |||||
public BaseEntity() { | |||||
} | |||||
/* | |||||
* implements Cloneable | |||||
*/ | |||||
@Override | |||||
protected Object clone() throws CloneNotSupportedException { | |||||
BaseEntity<?> clone = (BaseEntity<?>) super.clone(); | |||||
clone.id = null; | |||||
return clone; | |||||
} | |||||
/* | |||||
* getters and setters | |||||
*/ | |||||
public PK getId() { | |||||
return id; | |||||
} | |||||
@SuppressWarnings("unchecked") | |||||
public void setId(Serializable id) { | |||||
this.id = (PK) id; | |||||
} | |||||
public int getVersionId() { | |||||
return versionId; | |||||
} | |||||
public void setVersionId(int versionId) { | |||||
this.versionId = versionId; | |||||
} | |||||
public boolean isDeleted() { | |||||
return deleted; | |||||
} | |||||
public void setDeleted(boolean deleted) { | |||||
this.deleted = deleted; | |||||
} | |||||
public Integer getCreatedBy() { | |||||
return createdBy; | |||||
} | |||||
public void setCreatedBy(Integer createdBy) { | |||||
this.createdBy = createdBy; | |||||
} | |||||
public Integer getModifiedBy() { | |||||
return modifiedBy; | |||||
} | |||||
public void setModifiedBy(Integer modifiedBy) { | |||||
this.modifiedBy = modifiedBy; | |||||
} | |||||
public Date getCreated() { | |||||
return created; | |||||
} | |||||
public void setCreated(Date created) { | |||||
this.created = created; | |||||
} | |||||
public Date getModified() { | |||||
return modified; | |||||
} | |||||
public void setModified(Date modified) { | |||||
this.modified = modified; | |||||
} | |||||
public Integer getSysGroupId() { | |||||
return sysGroupId; | |||||
} | |||||
public void setSysGroupId(Integer sysGroupId) { | |||||
this.sysGroupId = sysGroupId; | |||||
} | |||||
} |
@@ -0,0 +1,107 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core; | |||||
import java.io.Serializable; | |||||
import java.util.Date; | |||||
public interface IBaseEntity<PK extends Serializable> extends Serializable, Cloneable { | |||||
/** | |||||
* @return the entity id | |||||
*/ | |||||
public PK getId(); | |||||
/** | |||||
* Set the entity id | |||||
* | |||||
* @param id | |||||
* the entity id | |||||
*/ | |||||
public void setId(PK id); | |||||
/** | |||||
* @return hibernate version id | |||||
*/ | |||||
public int getVersionId(); | |||||
/** | |||||
* Set the hibernate version id | |||||
* | |||||
* @param versionId | |||||
* the hibernate version id | |||||
*/ | |||||
public void setVersionId(int versionId); | |||||
/** | |||||
* @return entity's deleted flag | |||||
*/ | |||||
public boolean isDeleted(); | |||||
/** | |||||
* Set the entity's deleted flag | |||||
* | |||||
* @param deleted | |||||
* the entity's deleted flag | |||||
*/ | |||||
public void setDeleted(boolean deleted); | |||||
/** | |||||
* @return created by User ID | |||||
*/ | |||||
public Integer getCreatedBy(); | |||||
/** | |||||
* Set the created by User ID | |||||
* | |||||
* @param createdBy | |||||
* the created by User ID | |||||
*/ | |||||
public void setCreatedBy(Integer createdBy); | |||||
/** | |||||
* @return the last modified by User ID | |||||
*/ | |||||
public Integer getModifiedBy(); | |||||
/** | |||||
* Set the last modified by User ID | |||||
* | |||||
* @param modifiedBy | |||||
* the last modified by User ID | |||||
*/ | |||||
public void setModifiedBy(Integer modifiedBy); | |||||
/** | |||||
* @return created date | |||||
*/ | |||||
public Date getCreated(); | |||||
/** | |||||
* Set the created date | |||||
* | |||||
* @param created | |||||
* the created date | |||||
*/ | |||||
public void setCreated(Date created); | |||||
/** | |||||
* @return the last modified date | |||||
*/ | |||||
public Date getModified(); | |||||
/** | |||||
* Set the last modified date | |||||
* | |||||
* @param modified | |||||
* the last modified date | |||||
*/ | |||||
public void setModified(Date modified); | |||||
} |
@@ -0,0 +1,36 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
/** | |||||
* Get or Set Session Attributes | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class Session { | |||||
public static final String AVAILABLE_LOCALES = "availableLocales"; | |||||
/** | |||||
* Get session attribute | |||||
*/ | |||||
public static Object getAttribute(HttpServletRequest request, String name) { | |||||
return request.getSession().getAttribute(name); | |||||
} | |||||
/** | |||||
* Set session attribute | |||||
*/ | |||||
public static void setAttribute(HttpServletRequest request, String name, Object value) { | |||||
request.getSession().setAttribute(name, value); | |||||
} | |||||
} |
@@ -0,0 +1,149 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core; | |||||
import com.ffii.core.setting.service.SettingsService; | |||||
import com.ffii.core.utils.BooleanUtils; | |||||
public abstract class Settings { | |||||
/* | |||||
* System-wide settings | |||||
*/ | |||||
/** Define all available language names as comma separated string */ | |||||
public static final String SYS_AVAILABLE_LANGUAGES = "SYS.availableLanguages"; | |||||
/** Define all available locales as comma separated string */ | |||||
public static final String SYS_AVAILABLE_LOCALES = "SYS.availableLocales"; | |||||
/** Define the system default locale as string */ | |||||
public static final String SYS_DEFAULT_LOCALE = "SYS.defaultLocale"; | |||||
/** Define the system available currencies as comma separated string */ | |||||
public static final String SYS_CURRENCIES = "SYS.currencies"; | |||||
/** Define the system modules (authorities.module) */ | |||||
public static final String SYS_ROLE_MODULES = "SYS.modules"; | |||||
/* | |||||
* Mail settings | |||||
*/ | |||||
/** Mail - SMTP host */ | |||||
public static final String MAIL_SMTP_HOST = "MAIL.smtp.host"; | |||||
/** Mail - SMTP port */ | |||||
public static final String MAIL_SMTP_PORT = "MAIL.smtp.port"; | |||||
/** Mail - SMTP username */ | |||||
public static final String MAIL_SMTP_USERNAME = "MAIL.smtp.username"; | |||||
/** Mail - SMTP password */ | |||||
public static final String MAIL_SMTP_PASSWORD = "MAIL.smtp.password"; | |||||
public static final String SYS_PASSWORD_RULE_MIN = "SYS.password.rule.length.min"; | |||||
public static final String SYS_PASSWORD_RULE_MAX = "SYS.password.rule.length.max"; | |||||
public static final String SYS_PASSWORD_RULE_NUMBER = "SYS.password.rule.number"; | |||||
public static final String SYS_PASSWORD_RULE_UPPER_ENG = "SYS.password.rule.upper.eng"; | |||||
public static final String SYS_PASSWORD_RULE_LOWER_ENG = "SYS.password.rule.lower.eng"; | |||||
public static final String SYS_PASSWORD_RULE_SPECIAL = "SYS.password.rule.special"; | |||||
public static final String JS_VERSION = "JS.version"; | |||||
public static class MailSMTP { | |||||
private String host; | |||||
private String port; | |||||
private String username; | |||||
private String password; | |||||
public MailSMTP(SettingsService settingsService) { | |||||
if (settingsService == null) throw new NullPointerException("SettingsService cannot be null"); | |||||
this.host = settingsService.getString(MAIL_SMTP_HOST); | |||||
this.port = settingsService.getString(MAIL_SMTP_PORT); | |||||
this.username = settingsService.getString(MAIL_SMTP_USERNAME); | |||||
this.password = settingsService.getString(MAIL_SMTP_PASSWORD); | |||||
} | |||||
public String getHost() { | |||||
return host; | |||||
} | |||||
public String getPort() { | |||||
return port; | |||||
} | |||||
public String getUsername() { | |||||
return username; | |||||
} | |||||
public String getPassword() { | |||||
return password; | |||||
} | |||||
} | |||||
public static class PasswordRule { | |||||
private int min; | |||||
private int max; | |||||
private boolean number; | |||||
private boolean upperEng; | |||||
private boolean lowerEng; | |||||
private boolean specialChar; | |||||
public PasswordRule(SettingsService settingsService) { | |||||
if (settingsService == null) throw new NullPointerException("SettingsService cannot be null"); | |||||
this.min = settingsService.getInt(SYS_PASSWORD_RULE_MIN); | |||||
this.max = settingsService.getInt(SYS_PASSWORD_RULE_MAX); | |||||
this.number = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_NUMBER)); | |||||
this.upperEng = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_UPPER_ENG)); | |||||
this.lowerEng = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_LOWER_ENG)); | |||||
this.specialChar = BooleanUtils.isTrue(settingsService.getString(SYS_PASSWORD_RULE_SPECIAL)); | |||||
} | |||||
public int getMin() { | |||||
return min; | |||||
} | |||||
public int getMax() { | |||||
return max; | |||||
} | |||||
public boolean needNumberChar() { | |||||
return number; | |||||
} | |||||
public boolean needUpperEngChar() { | |||||
return upperEng; | |||||
} | |||||
public boolean needLowerEngChar() { | |||||
return lowerEng; | |||||
} | |||||
public boolean needSpecialChar() { | |||||
return specialChar; | |||||
} | |||||
public String getWrongMsg() { | |||||
StringBuilder msg = new StringBuilder("Please Following Password Rule.\n"); | |||||
msg.append("Minimum " + getMin() + " Characters\n"); | |||||
msg.append("Maximum " + getMax() + " Characters\n"); | |||||
if (needNumberChar()) msg.append("Numbers\n"); | |||||
if (needLowerEngChar()) msg.append("Lower-Case Letters\n"); | |||||
if (needUpperEngChar()) msg.append("Capital Letters\n"); | |||||
if (needSpecialChar()) msg.append("Symbols\n"); | |||||
return msg.toString(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,264 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core; | |||||
import java.util.List; | |||||
import javax.persistence.Column; | |||||
import javax.persistence.Entity; | |||||
import javax.persistence.Table; | |||||
import javax.persistence.Transient; | |||||
import org.springframework.security.core.GrantedAuthority; | |||||
import org.springframework.security.core.userdetails.UserDetails; | |||||
@Entity | |||||
@Table(name = "users") | |||||
public class User extends BaseEntity<Integer> implements UserDetails { | |||||
private static final long serialVersionUID = -9036483598847846102L; | |||||
/** Company ID */ | |||||
@Column(nullable = false) | |||||
private int companyId; | |||||
/** Customer ID, optional, for End User's customers */ | |||||
@Column(nullable = true) | |||||
private Integer customerId; | |||||
/** username for login */ | |||||
@Column(columnDefinition = "varchar(32)", nullable = false) | |||||
private String username; | |||||
/** password for login */ | |||||
@Column(columnDefinition = "varchar(64)", nullable = false) | |||||
private String password; | |||||
/** is user expired? (not implemented) */ | |||||
@Column(columnDefinition = "boolean", nullable = false) | |||||
private boolean expired; | |||||
/** is user locked? (not implemented) */ | |||||
@Column(columnDefinition = "boolean", nullable = false) | |||||
private boolean locked; | |||||
/** User default locale (en / zh_TW / zh_CN) */ | |||||
@Column(columnDefinition = "varchar(5)") | |||||
private String locale; | |||||
/** Full name for display */ | |||||
@Column(columnDefinition = "varchar(90)") | |||||
private String fullname; | |||||
/* User profile info (optional fields) START */ | |||||
/** First name */ | |||||
@Column(columnDefinition = "varchar(45)") | |||||
private String firstname; | |||||
/** Last name */ | |||||
@Column(columnDefinition = "varchar(30)") | |||||
private String lastname; | |||||
/** Department */ | |||||
@Column(nullable = true) | |||||
private Integer deptId; | |||||
/** Job title */ | |||||
@Column(columnDefinition = "varchar(60)") | |||||
private String title; | |||||
/** Email */ | |||||
@Column(columnDefinition = "varchar(120)") | |||||
private String email; | |||||
/** Phone 1 */ | |||||
@Column(columnDefinition = "varchar(30)") | |||||
private String phone1; | |||||
/** Phone 2 */ | |||||
@Column(columnDefinition = "varchar(30)") | |||||
private String phone2; | |||||
/** Remarks */ | |||||
@Column(columnDefinition = "varchar(600)") | |||||
private String remarks; | |||||
/* User profile info (optional fields) END */ | |||||
/** Transient authorities (populated during user authentication) */ | |||||
@Transient | |||||
private List<GrantedAuthority> authorities; | |||||
/* | |||||
* getters and setters | |||||
*/ | |||||
public int getCompanyId() { | |||||
return companyId; | |||||
} | |||||
public void setCompanyId(int companyId) { | |||||
this.companyId = companyId; | |||||
} | |||||
public Integer getCustomerId() { | |||||
return customerId; | |||||
} | |||||
public void setCustomerId(Integer customerId) { | |||||
this.customerId = customerId; | |||||
} | |||||
public String getUsername() { | |||||
return username; | |||||
} | |||||
public void setUsername(String username) { | |||||
this.username = username; | |||||
} | |||||
public String getPassword() { | |||||
return password; | |||||
} | |||||
public void setPassword(String password) { | |||||
this.password = password; | |||||
} | |||||
public boolean isExpired() { | |||||
return expired; | |||||
} | |||||
public void setExpired(boolean expired) { | |||||
this.expired = expired; | |||||
} | |||||
public boolean isLocked() { | |||||
return locked; | |||||
} | |||||
public void setLocked(boolean locked) { | |||||
this.locked = locked; | |||||
} | |||||
public String getLocale() { | |||||
return locale; | |||||
} | |||||
public void setLocale(String locale) { | |||||
this.locale = locale; | |||||
} | |||||
public String getFullname() { | |||||
return fullname; | |||||
} | |||||
public void setFullname(String fullname) { | |||||
this.fullname = fullname; | |||||
} | |||||
public String getFirstname() { | |||||
return firstname; | |||||
} | |||||
public void setFirstname(String firstname) { | |||||
this.firstname = firstname; | |||||
} | |||||
public String getLastname() { | |||||
return lastname; | |||||
} | |||||
public void setLastname(String lastname) { | |||||
this.lastname = lastname; | |||||
} | |||||
public Integer getDeptId() { | |||||
return deptId; | |||||
} | |||||
public void setDeptId(Integer deptId) { | |||||
this.deptId = deptId; | |||||
} | |||||
public String getTitle() { | |||||
return title; | |||||
} | |||||
public void setTitle(String title) { | |||||
this.title = title; | |||||
} | |||||
public String getEmail() { | |||||
return email; | |||||
} | |||||
public void setEmail(String email) { | |||||
this.email = email; | |||||
} | |||||
public String getPhone1() { | |||||
return phone1; | |||||
} | |||||
public void setPhone1(String phone1) { | |||||
this.phone1 = phone1; | |||||
} | |||||
public String getPhone2() { | |||||
return phone2; | |||||
} | |||||
public void setPhone2(String phone2) { | |||||
this.phone2 = phone2; | |||||
} | |||||
public String getRemarks() { | |||||
return remarks; | |||||
} | |||||
public void setRemarks(String remarks) { | |||||
this.remarks = remarks; | |||||
} | |||||
public void setAuthorities(List<GrantedAuthority> authorities) { | |||||
this.authorities = authorities; | |||||
} | |||||
/* | |||||
* Override | |||||
*/ | |||||
@Override | |||||
public List<GrantedAuthority> getAuthorities() { | |||||
return authorities; | |||||
} | |||||
@Override | |||||
public boolean isAccountNonExpired() { | |||||
return !expired; | |||||
} | |||||
@Override | |||||
public boolean isAccountNonLocked() { | |||||
return !locked; | |||||
} | |||||
@Override | |||||
public boolean isCredentialsNonExpired() { | |||||
return true; | |||||
} | |||||
@Override | |||||
public boolean isEnabled() { | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,160 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.dao; | |||||
import java.io.Serializable; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
import com.ffii.core.BaseEntity; | |||||
import com.ffii.core.User; | |||||
import com.ffii.core.utils.SecurityUtils; | |||||
import org.hibernate.query.Query; | |||||
import org.springframework.orm.hibernate5.support.HibernateDaoSupport; | |||||
/** | |||||
* Implementation of the {@link IHibernateDao} interface. | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class HibernateDao<T extends BaseEntity<PK>, PK extends Serializable> extends HibernateDaoSupport | |||||
implements IHibernateDao<T, PK> { | |||||
private final Class<T> type; | |||||
public HibernateDao(final Class<T> type) { | |||||
this.type = type; | |||||
} | |||||
/** | |||||
* Automatically set the {@code createdBy}, {@code created}, {@code modifiedBy}, | |||||
* and {@code modified} fields of the object instance. | |||||
* | |||||
* @param instance the object instance | |||||
* @param isNew {@code true} if the instance is new, and need to set the | |||||
* {@code createdBy} and {@code created} fields | |||||
*/ | |||||
private void autoSetCreateAndLastModifyFields(final T instance, boolean isNew) { | |||||
// try to get the authenticated User ID from session (not available in Mapp) | |||||
User user = SecurityUtils.getUser(); | |||||
Integer userId = null; | |||||
Integer sysGroupId = null; | |||||
if (user != null){ | |||||
userId = user.getId(); | |||||
sysGroupId = user.getSysGroupId(); | |||||
} | |||||
Date now = new Date(); | |||||
if (isNew) { | |||||
if (instance.getCreatedBy() == null && userId != null) | |||||
instance.setCreatedBy(userId); | |||||
if (userId != null) | |||||
instance.setModifiedBy(userId); | |||||
if (instance.getCreated() == null) | |||||
instance.setCreated(now); | |||||
if(instance.getSysGroupId() == null) | |||||
instance.setSysGroupId(sysGroupId); | |||||
instance.setModified(now); | |||||
} else { | |||||
if (userId != null) | |||||
instance.setModifiedBy(userId); | |||||
instance.setModified(now); | |||||
} | |||||
} | |||||
@SuppressWarnings("unchecked") | |||||
@Override | |||||
public PK save(final T instance) { | |||||
autoSetCreateAndLastModifyFields(instance, true); | |||||
return (PK) getHibernateTemplate().save(instance); | |||||
} | |||||
@SuppressWarnings("unchecked") | |||||
@Override | |||||
public PK saveAs(final T instance) { | |||||
return (PK) getHibernateTemplate().save(instance); | |||||
} | |||||
@Override | |||||
public void update(final T instance) { | |||||
autoSetCreateAndLastModifyFields(instance, false); | |||||
getHibernateTemplate().update(instance); | |||||
} | |||||
@Override | |||||
public PK saveOrUpdate(final T instance) { | |||||
Serializable id = instance.getId(); | |||||
if (id instanceof Long) { | |||||
if (((Long) id).longValue() < 0L) | |||||
id = null; | |||||
} else if (id instanceof Integer) { | |||||
if (((Integer) id).intValue() < 0) | |||||
id = null; | |||||
} | |||||
if (id == null) | |||||
return save(instance); | |||||
else { | |||||
update(instance); | |||||
return (PK) instance.getId(); | |||||
} | |||||
} | |||||
@Override | |||||
public boolean delete(final T instance) { | |||||
try { | |||||
getHibernateTemplate().delete(instance); | |||||
return true; | |||||
} catch (Exception e) { | |||||
return false; | |||||
} | |||||
} | |||||
@Override | |||||
public void deleteAll(List<T> instances) { | |||||
getHibernateTemplate().deleteAll(instances); | |||||
} | |||||
@Override | |||||
public void flush() { | |||||
getHibernateTemplate().flush(); | |||||
} | |||||
@Override | |||||
public T find(final PK id) { | |||||
return getHibernateTemplate().get(type, id); | |||||
} | |||||
@Override | |||||
@SuppressWarnings("unchecked") | |||||
public T findByQuery(final String queryString, final Serializable... args) { | |||||
return getHibernateTemplate().execute(session -> { | |||||
Query<T> query = session.createQuery(queryString); | |||||
for (int i = 0; i < args.length; i++) { | |||||
query.setParameter(i, args[i]); | |||||
} | |||||
query.setMaxResults(1); | |||||
return query.uniqueResult(); | |||||
}); | |||||
} | |||||
@Override | |||||
@SuppressWarnings("unchecked") | |||||
public List<T> listByQuery(final String queryString, final Serializable... args) { | |||||
return getHibernateTemplate().execute(session -> { | |||||
Query<T> query = session.createQuery(queryString); | |||||
for (int i = 0; i < args.length; i++) { | |||||
query.setParameter(i, args[i]); | |||||
} | |||||
return query.list(); | |||||
}); | |||||
} | |||||
} |
@@ -0,0 +1,125 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.dao; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
import org.springframework.orm.hibernate5.HibernateTemplate; | |||||
/** | |||||
* Hibernate DAO interface. For use with business services to perform actual operations. | |||||
* | |||||
* @param <T> | |||||
* The class this DAO is taking care of. | |||||
* @param <PK> | |||||
* The type of the primary key of class instances. | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public interface IHibernateDao<T extends Serializable, PK extends Serializable> { | |||||
/** | |||||
* Returns the underlying {@link HibernateTemplate} | |||||
*/ | |||||
public HibernateTemplate getHibernateTemplate(); | |||||
/** | |||||
* Saves the instance to the database. | |||||
* | |||||
* @param instance | |||||
* the object instance to save | |||||
* | |||||
* @return the object instance's primary key in database | |||||
*/ | |||||
public PK save(T instance); | |||||
/** | |||||
* Saves the instance to the database without setting the created/updated fields. | |||||
* | |||||
* @param instance | |||||
* the object instance to save | |||||
* | |||||
* @return the object instance's primary key in database | |||||
*/ | |||||
public PK saveAs(T instance); | |||||
/** | |||||
* Updates the record in the database with the given instance. | |||||
* | |||||
* @param instance | |||||
* modified object instance | |||||
*/ | |||||
public void update(T instance); | |||||
/** | |||||
* Saves or updates the record in the database with the given instance. | |||||
* | |||||
* @param instance | |||||
* new or modified object instance | |||||
* | |||||
* @return the object instance's primary key in database | |||||
*/ | |||||
public PK saveOrUpdate(T instance); | |||||
/** | |||||
* Deletes the object instance from the database. | |||||
* | |||||
* @param instance | |||||
* the object instance to remove | |||||
*/ | |||||
public boolean delete(T instance); | |||||
/** | |||||
* Remove a collection of instances from the database. | |||||
* | |||||
* @param instances | |||||
* The object instances to remove | |||||
*/ | |||||
public void deleteAll(List<T> instances); | |||||
/** | |||||
* Finds the object with the specified identity from the database. | |||||
* | |||||
* @param id | |||||
* the object's primary key in database | |||||
* | |||||
* @return the mapped object instance from the database | |||||
*/ | |||||
public T find(PK id); | |||||
/** | |||||
* Finds the object using the specified query (HQL) and arguments from the database. | |||||
* | |||||
* @param queryString | |||||
* the query string using HQL | |||||
* @param args | |||||
* variable number of arguments to pass to the query. Note you must have the exact number of arguments as specified in the query. | |||||
* | |||||
* @return the object instance from the database | |||||
*/ | |||||
public T findByQuery(String queryString, Serializable... args); | |||||
/** | |||||
* Returns a {@code List} of {@code Object}s using the specified query (HQL) and arguments from the database. | |||||
* | |||||
* @param queryString | |||||
* the query string using HQL | |||||
* @param args | |||||
* variable number of arguments to pass to the query. Note you must have the exact number of arguments as specified in the query. | |||||
* | |||||
* @return {@code List} of object instances from the database | |||||
*/ | |||||
public List<T> listByQuery(String queryString, Serializable... args); | |||||
public void flush(); | |||||
} |
@@ -0,0 +1,260 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.dao; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import javax.persistence.Table; | |||||
import com.ffii.core.BaseEntity; | |||||
import com.ffii.core.utils.MapUtils; | |||||
import com.ffii.core.utils.Params; | |||||
import org.springframework.dao.IncorrectResultSizeDataAccessException; | |||||
import org.springframework.jdbc.core.BeanPropertyRowMapper; | |||||
import org.springframework.jdbc.core.JdbcOperations; | |||||
import org.springframework.jdbc.core.JdbcTemplate; | |||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport; | |||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | |||||
import org.springframework.jdbc.core.simple.SimpleJdbcInsert; | |||||
/** | |||||
* JDBC DAO implementation which extends Spring {@link NamedParameterJdbcDaoSupport} | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class JdbcDao extends NamedParameterJdbcDaoSupport { | |||||
/** | |||||
* Return an instance of SimpleJdbcInsert with the specified table name | |||||
*/ | |||||
private SimpleJdbcInsert getJdbcInsert(String tableName, String idName) { | |||||
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(getJdbcTemplate()); | |||||
if (idName != null) | |||||
return jdbcInsert.withTableName(tableName).usingGeneratedKeyColumns(idName); | |||||
else | |||||
return jdbcInsert.withTableName(tableName); | |||||
} | |||||
/** | |||||
* Query for a {@code List} of {@code Object}s of type {@code T} using the supplied {@code class} to map the query results. Uses SQL with the named | |||||
* parameter support provided by the {@code NamedParameterJdbcTemplate}. | |||||
* | |||||
* @param sql | |||||
* the SQL query to run | |||||
* @param mappedClass | |||||
* the class to use for result mapping | |||||
* @param args | |||||
* the map containing the arguments for the query | |||||
* @param maxRows | |||||
* the maximum results to return | |||||
*/ | |||||
public <T> List<T> query(String sql, Class<T> mappedClass, Map<String, ?> args, int maxRows) { | |||||
((JdbcTemplate) getNamedParameterJdbcTemplate().getJdbcOperations()).setMaxRows(maxRows); | |||||
return getNamedParameterJdbcTemplate().query(sql, args, BeanPropertyRowMapper.newInstance(mappedClass)); | |||||
} | |||||
/** | |||||
* Execute the supplied query with the supplied arguments. | |||||
* <p> | |||||
* The query is expected to be a single row query (otherwise, {@code null} will be returned); the result row will be mapped to a Map<String, Object> (one | |||||
* entry for each column, using the column name as the key). Uses SQL with the named parameter support provided by the {@link NamedParameterJdbcTemplate} | |||||
* </p> | |||||
* | |||||
* @param sql | |||||
* the SQL query to run | |||||
* @param args | |||||
* the map containing the arguments for the query | |||||
*/ | |||||
public Map<String, Object> queryForMap(String sql, Map<String, ?> args) { | |||||
try { | |||||
return getNamedParameterJdbcTemplate().queryForMap(sql, args); | |||||
} catch (IncorrectResultSizeDataAccessException e) { | |||||
logger.debug("Unexpected row count for queryForMap()", e); | |||||
return null; | |||||
} | |||||
} | |||||
/** | |||||
* Query for a {@code List} of {@code Map}s, each element in the returned {@code List} is constructed as a {@code Map} as described in | |||||
* {@link JdbcOperations#queryForMap}. Uses SQL with the named parameter support provided by the {@code NamedParameterJdbcTemplate}. | |||||
* | |||||
* @param sql | |||||
* the SQL query to run | |||||
* @param args | |||||
* the map containing the arguments for the query | |||||
* @param maxRows | |||||
* the maximum results to return | |||||
*/ | |||||
public List<Map<String, Object>> queryForList(String sql, Map<String, ?> args, int maxRows) { | |||||
((JdbcTemplate) getNamedParameterJdbcTemplate().getJdbcOperations()).setMaxRows(maxRows); | |||||
return getNamedParameterJdbcTemplate().queryForList(sql, args); | |||||
} | |||||
/** | |||||
* Query for a {@code List} of {@code Map}s, each element in the returned {@code List} is constructed as a {@code Map} as described in | |||||
* {@link JdbcOperations#queryForMap}. Uses SQL with the named parameter support provided by the {@code NamedParameterJdbcTemplate}. | |||||
* | |||||
* @param sql | |||||
* the SQL query to run | |||||
* @param args | |||||
* the map containing the arguments for the query | |||||
*/ | |||||
public List<Map<String, Object>> queryForList(String sql, Map<String, ?> args) { | |||||
return queryForList(sql, args, 0); | |||||
} | |||||
/** | |||||
* Query for an {@code int} passing in a SQL query using the named parameter support provided by the {@code NamedParameterJdbcTemplate} and a map containing | |||||
* the arguments. | |||||
* <p> | |||||
* The query is expected to be a single row/single column query; the returned result will be directly mapped to an {@code int}. | |||||
* | |||||
* @param sql | |||||
* the SQL query to run | |||||
* @param args | |||||
* the map containing the arguments for the query | |||||
*/ | |||||
public int queryForInt(String sql, Map<String, ?> args) { | |||||
Integer value = getNamedParameterJdbcTemplate().queryForObject(sql, args, Integer.class); | |||||
return (value != null ? value.intValue() : 0); | |||||
} | |||||
/** | |||||
* Query for a {@code String} passing in a SQL query using the named parameter support provided by the {@code NamedParameterJdbcTemplate} and a map | |||||
* containing the arguments. | |||||
* <p> | |||||
* The query is expected to be a single row/single column query; the returned result will be directly mapped to a {@code String}. | |||||
* | |||||
* @param sql | |||||
* the SQL query to run | |||||
* @param args | |||||
* the map containing the arguments for the query | |||||
*/ | |||||
public String queryForString(String sql, Map<String, ?> args) { | |||||
return getNamedParameterJdbcTemplate().queryForObject(sql, args, String.class); | |||||
} | |||||
/** | |||||
* Executes a batch using the supplied SQL statement with the batch of supplied arguments using the named parameter support provided by the | |||||
* {@code NamedParameterJdbcTemplate} | |||||
* | |||||
* @param sql | |||||
* the SQL statement to execute | |||||
* @param batchValues | |||||
* the array of Maps containing the batch of arguments for the query | |||||
* | |||||
* @return an array containing the numbers of rows affected by each update in the batch | |||||
*/ | |||||
public int[] executeBatchUpdate(String sql, Map<String, ?>[] batchValues) { | |||||
return getNamedParameterJdbcTemplate().batchUpdate(sql, batchValues); | |||||
} | |||||
/** | |||||
* Execute the supplied SQL statement with (optional) supplied arguments using the named parameter support provided by the | |||||
* {@code NamedParameterJdbcTemplate} | |||||
* | |||||
* @param sql | |||||
* the SQL statement to execute | |||||
* @param args | |||||
* the map containing the arguments for the query | |||||
* | |||||
* @return the number of rows affected by the update | |||||
*/ | |||||
public int executeUpdate(String sql, Map<String, ?> args) { | |||||
return getNamedParameterJdbcTemplate().update(sql, args); | |||||
} | |||||
/** | |||||
* Execute the supplied SQL statement with no arguments | |||||
* | |||||
* @param sql | |||||
* the SQL statement to execute | |||||
* | |||||
* @return the number of rows affected by the update | |||||
*/ | |||||
public int executeUpdate(String sql) { | |||||
return getJdbcTemplate().update(sql); | |||||
} | |||||
/** | |||||
* Executes SQL UPDATE statement with the supplied class and its ID and keyValuePairs | |||||
* | |||||
* @param clazz | |||||
* any class that extends BaseEntity | |||||
* @param keyValuePairs | |||||
* key value pairs, must include the ID field | |||||
* | |||||
* @return the number of rows affected by the update | |||||
*/ | |||||
public int executeUpdateByTable(String tableName, Map<String, ?> args) { | |||||
StringBuilder sql = new StringBuilder("UPDATE `" + tableName + "` SET "); | |||||
int i = 0; | |||||
for (String key : args.keySet()) { | |||||
if (i > 0) sql.append(", "); | |||||
sql.append(key + " = :" + key); | |||||
i++; | |||||
} | |||||
sql.append(" WHERE id = :id"); | |||||
return executeUpdate(sql.toString(), args); | |||||
} | |||||
/** | |||||
* Execute SQL Insert statement with the supplied arguments | |||||
* | |||||
* @param tableName | |||||
* the database table name | |||||
* @param args | |||||
* the map containing the arguments for the insert statement in the form of column name and its value | |||||
* | |||||
* @return the number of rows affected as returned by the JDBC driver | |||||
*/ | |||||
public int executeInsert(String tableName, Map<String, Object> args) { | |||||
return getJdbcInsert(tableName, null).execute(args); | |||||
} | |||||
/** | |||||
* Execute SQL Insert statement with the supplied arguments and return the generated key value | |||||
* | |||||
* @param tableName | |||||
* the database table name | |||||
* @param idName | |||||
* the name of ID column that has auto generated key | |||||
* @param args | |||||
* the map containing the arguments for the insert statement in the form of column name and its value | |||||
* | |||||
* @return the generated key value | |||||
*/ | |||||
public Number executeInsertAndReturnKey(String tableName, String idName, Map<String, Object> args) { | |||||
return getJdbcInsert(tableName, idName).executeAndReturnKey(args); | |||||
} | |||||
/** | |||||
* Executes SQL Delete statement with the supplied class and its ID | |||||
* | |||||
* @param clazz | |||||
* any class that extends BaseEntity | |||||
* @param id | |||||
* the entity's ID | |||||
* | |||||
* @return the number of rows affected by the delete | |||||
*/ | |||||
public int executeDelete(Class<? extends BaseEntity<Serializable>> clazz, Serializable id) { | |||||
Table table = clazz.getAnnotation(Table.class); | |||||
String sql = "DELETE FROM " + table.name() + " WHERE id = :id"; | |||||
return executeUpdate(sql, MapUtils.toHashMap(Params.ID, id)); | |||||
} | |||||
} |
@@ -0,0 +1,56 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.engine; | |||||
import java.io.IOException; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Component; | |||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; | |||||
import org.springframework.web.context.support.WebApplicationObjectSupport; | |||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; | |||||
import freemarker.template.Template; | |||||
import freemarker.template.TemplateException; | |||||
/** | |||||
* FreeMarker template engine - a Spring Component Bean | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
@Component | |||||
public class FreeMarkerEngine extends WebApplicationObjectSupport { | |||||
@Autowired | |||||
private FreeMarkerConfigurer freemarkerConfig; | |||||
/** | |||||
* Generates content from FreeMarker template | |||||
* | |||||
* @param template | |||||
* the FreeMarker template name (e.g. {@code "example/example.ftl"}) | |||||
* @param model | |||||
* the model to be applied to the FreeMarker template, may be null | |||||
* @return the generated content as a {@code String}, or {@code null} if exceptions occured | |||||
*/ | |||||
public String generateFreeMarkerContent(String template, Object model) { | |||||
try { | |||||
Template t = freemarkerConfig.getConfiguration().getTemplate(template); | |||||
return FreeMarkerTemplateUtils.processTemplateIntoString(t, model); | |||||
} catch (TemplateException e) { | |||||
logger.error("Error while processing FreeMarker template ", e); | |||||
} catch (IOException e) { | |||||
logger.error("IOException occured ", e); | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,316 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2018 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.engine; | |||||
import java.io.File; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.Properties; | |||||
import javax.mail.MessagingException; | |||||
import javax.mail.internet.InternetAddress; | |||||
import javax.mail.internet.MimeMessage; | |||||
import com.ffii.core.Settings; | |||||
import com.ffii.core.setting.service.SettingsService; | |||||
import com.ffii.core.utils.SecurityUtils; | |||||
import com.ffii.tbms.user.service.SysGroupService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.core.io.InputStreamSource; | |||||
import org.springframework.mail.javamail.JavaMailSender; | |||||
import org.springframework.mail.javamail.JavaMailSenderImpl; | |||||
import org.springframework.mail.javamail.MimeMessageHelper; | |||||
import org.springframework.stereotype.Component; | |||||
import org.springframework.web.context.support.WebApplicationObjectSupport; | |||||
/** | |||||
* Mail Engine is a Spring Component Bean that handles E-mail operations. | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
@Component | |||||
public class MailEngine extends WebApplicationObjectSupport { | |||||
@Autowired | |||||
private FreeMarkerEngine freeMarkerEngine; | |||||
private JavaMailSender mailSender; | |||||
@Autowired | |||||
private SettingsService settingsService; | |||||
@Autowired | |||||
private SysGroupService sysGroupService; | |||||
/** | |||||
* Send HTML mail message | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param cc | |||||
* the cc addresses, may be null | |||||
* @param bcc | |||||
* the bcc addresses, may be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param html | |||||
* the html message body, must not be null | |||||
* @param attachmentFilename | |||||
* the name of the attachment as it will appear in the mail | |||||
* @param file | |||||
* the File to attach | |||||
*/ | |||||
private JavaMailSender getJavaMailSender(int sysGroupId){ | |||||
//if(mailSender == null){ | |||||
Map<String, Object> sysGroup = sysGroupService.find(sysGroupId); | |||||
String smtp_host = ""; | |||||
int smtp_port = 0; | |||||
String smtp_username = ""; | |||||
String smtp_password = ""; | |||||
logger.info(sysGroup); | |||||
if(sysGroup != null){ | |||||
smtp_host = sysGroup.get("smtp_host")==null?"":sysGroup.get("smtp_host").toString(); | |||||
smtp_port = sysGroup.get("smtp_port")==null?0:Integer.parseInt(sysGroup.get("smtp_port").toString()); | |||||
smtp_username = sysGroup.get("smtp_username")==null?"":sysGroup.get("smtp_username").toString(); | |||||
smtp_password = sysGroup.get("smtp_password")==null?"":sysGroup.get("smtp_password").toString(); | |||||
} | |||||
JavaMailSenderImpl jmsi = new JavaMailSenderImpl(); | |||||
jmsi.setHost(smtp_host); | |||||
jmsi.setPort(smtp_port); | |||||
jmsi.setUsername(smtp_username); | |||||
jmsi.setPassword(smtp_password); | |||||
Properties props = jmsi.getJavaMailProperties(); | |||||
props.put("mail.transport.protocol", "smtp"); | |||||
props.put("mail.smtp.auth", "true"); | |||||
props.put("mail.smtp.starttbms.enable", "true"); | |||||
props.put("mail.debug", "true"); | |||||
mailSender = jmsi; | |||||
//} | |||||
return mailSender; | |||||
} | |||||
public void sendHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, | |||||
String subject, String html, String attachmentFilename, File file) { | |||||
MimeMessage mimeMessage = getJavaMailSender(sysGroupId).createMimeMessage(); | |||||
try { | |||||
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8"); | |||||
message.setFrom(from); | |||||
if (replyTo != null) message.setReplyTo(replyTo); | |||||
message.setTo(to); | |||||
if (cc != null) message.setCc(cc); | |||||
if (bcc != null) message.setBcc(bcc); | |||||
if (subject != null) message.setSubject(subject); | |||||
message.setText(html, true); | |||||
if (attachmentFilename != null && file != null) message.addAttachment(attachmentFilename, file); | |||||
} catch (MessagingException e) { | |||||
logger.error("Failed to send HTML mail", e); | |||||
} | |||||
getJavaMailSender(sysGroupId).send(mimeMessage); | |||||
} | |||||
/** | |||||
* Send HTML mail message | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param html | |||||
* the html message body, must not be null | |||||
* @param attachmentFilename | |||||
* the name of the attachment as it will appear in the mail | |||||
* @param file | |||||
* the File to attach | |||||
*/ | |||||
public void sendHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||||
String subject, String html, String attachmentFilename, File file) { | |||||
sendHtmlMail(sysGroupId, from, replyTo, to, null, null, subject, html, attachmentFilename, file); | |||||
} | |||||
/** | |||||
* Send HTML mail message | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param html | |||||
* the html message body, must not be null | |||||
*/ | |||||
public void sendHtmlMail(int sysGroupId,InternetAddress from, InternetAddress replyTo, InternetAddress[] to, String subject, String html) { | |||||
sendHtmlMail(sysGroupId, from, replyTo, to, null, null, subject, html, null, null); | |||||
} | |||||
/** | |||||
* Send HTML mail message generated from FreeMarker template | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param cc | |||||
* the cc addresses, may be null | |||||
* @param bcc | |||||
* the bcc addresses, may be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param template | |||||
* the FreeMarker template name, must not be null | |||||
* @param model | |||||
* the model to be applied to the FreeMarker template, may be null | |||||
* @param attachmentFilename | |||||
* the name of the attachment as it will appear in the mail | |||||
* @param file | |||||
* the File to attach | |||||
*/ | |||||
public void sendFreeMarkerHtmlMail(int sysGroupId,InternetAddress from, InternetAddress replyTo, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, | |||||
String subject, String template, Map<?, ?> model, String attachmentFilename, File file) { | |||||
String html = freeMarkerEngine.generateFreeMarkerContent(template, model); | |||||
sendHtmlMail(sysGroupId,from, replyTo, to, cc, bcc, subject, html, attachmentFilename, file); | |||||
} | |||||
/** | |||||
* Send HTML mail message generated from FreeMarker template | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param template | |||||
* the FreeMarker template name, must not be null | |||||
* @param model | |||||
* the model to be applied to the FreeMarker template, may be null | |||||
*/ | |||||
public void sendFreeMarkerHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, | |||||
String subject, String template, Map<?, ?> model) { | |||||
sendFreeMarkerHtmlMail(sysGroupId, from, replyTo, to, cc, bcc, subject, template, model, null, null); | |||||
} | |||||
/** | |||||
* Send HTML mail message generated from FreeMarker template | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param template | |||||
* the FreeMarker template name, must not be null | |||||
* @param model | |||||
* the model to be applied to the FreeMarker template, may be null | |||||
* @param attachmentFilename | |||||
* the name of the attachment as it will appear in the mail | |||||
* @param file | |||||
* the File to attach | |||||
*/ | |||||
public void sendFreeMarkerHtmlMail(int sysGroupId,InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||||
String subject, String template, Map<?, ?> model, String attachmentFilename, File file) { | |||||
sendFreeMarkerHtmlMail(sysGroupId, from, replyTo, to, null, null, subject, template, model, attachmentFilename, file); | |||||
} | |||||
/** | |||||
* Send HTML mail message generated from FreeMarker template | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param template | |||||
* the FreeMarker template name, must not be null | |||||
* @param model | |||||
* the model to be applied to the FreeMarker template, may be null | |||||
* @param attachmentFilename | |||||
* the name of the attachment as it will appear in the mail | |||||
* @param file | |||||
* the File to attach | |||||
*/ | |||||
public void sendFreeMarkerHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||||
String subject, String template, Map<?, ?> model, String attachmentFilename, InputStreamSource file) { | |||||
String html = freeMarkerEngine.generateFreeMarkerContent(template, model); | |||||
MimeMessage mimeMessage = getJavaMailSender(sysGroupId).createMimeMessage(); | |||||
try { | |||||
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8"); | |||||
message.setFrom(from); | |||||
if (replyTo != null) message.setReplyTo(replyTo); | |||||
message.setTo(to); | |||||
if (subject != null) message.setSubject(subject); | |||||
message.setText(html, true); | |||||
if (attachmentFilename != null && file != null) message.addAttachment(attachmentFilename, file); | |||||
} catch (MessagingException e) { | |||||
logger.error("Failed to send HTML mail", e); | |||||
} | |||||
getJavaMailSender(sysGroupId).send(mimeMessage); | |||||
} | |||||
/** | |||||
* Send HTML mail message generated from FreeMarker template | |||||
* | |||||
* @param from | |||||
* the from address, must not be null | |||||
* @param replyTo | |||||
* the reply to address, may be null | |||||
* @param to | |||||
* the to addresses, must not be null | |||||
* @param subject | |||||
* the subject, may be null | |||||
* @param template | |||||
* the FreeMarker template name, must not be null | |||||
* @param model | |||||
* the model to be applied to the FreeMarker template, may be null | |||||
*/ | |||||
public void sendFreeMarkerHtmlMail(int sysGroupId, InternetAddress from, InternetAddress replyTo, InternetAddress[] to, | |||||
String subject, String template, Map<?, ?> model) { | |||||
sendFreeMarkerHtmlMail(sysGroupId,from, replyTo, to, null, null, subject, template, model, null, null); | |||||
} | |||||
} |
@@ -0,0 +1,109 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.i18n.support; | |||||
import java.text.MessageFormat; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Locale; | |||||
import java.util.Map; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.context.support.AbstractMessageSource; | |||||
import com.ffii.core.dao.JdbcDao; | |||||
/** | |||||
* Jdbc MessageSource implementation. | |||||
* | |||||
* <pre> | |||||
* CREATE TABLE `i18n` ( | |||||
* `locale` varchar(10) NOT NULL, | |||||
* `code` varchar(100) NOT NULL, | |||||
* `value` varchar(500) DEFAULT NULL, | |||||
* PRIMARY KEY (`locale`,`code`) | |||||
* ); | |||||
* </pre> | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class JdbcMessageSource extends AbstractMessageSource { | |||||
@Autowired | |||||
private JdbcDao jdbcDao; | |||||
@Override | |||||
protected String resolveCodeWithoutArguments(String code, Locale locale) { | |||||
return getMessages(locale.toString()).get(code); | |||||
} | |||||
@Override | |||||
protected MessageFormat resolveCode(String code, Locale locale) { | |||||
String msg = getMessages(locale.toString()).get(code); | |||||
return createMessageFormat(msg, locale); | |||||
} | |||||
/** i18n messages cache */ | |||||
private static Map<String, Map<String, String>> MESSAGES = new HashMap<String, Map<String, String>>(); | |||||
/** | |||||
* Load a list of messages from i18n table in database | |||||
* | |||||
* @param locale | |||||
* the locale String (e.g. {@code en_US}) | |||||
*/ | |||||
public List<Map<String, Object>> loadMessages(String locale) { | |||||
/* prep args */ | |||||
Map<String, Object> args = new HashMap<String, Object>(); | |||||
args.put("locale", locale); | |||||
/* build sql */ | |||||
String sql = "SELECT locale, code, value FROM i18n WHERE locale = :locale"; | |||||
/* do query and return */ | |||||
return jdbcDao.queryForList(sql, args); | |||||
} | |||||
/** | |||||
* Get i18n messages map | |||||
* | |||||
* @param locale | |||||
* the locale String (e.g. {@code en_US}) | |||||
*/ | |||||
public Map<String, String> getMessages(String locale) { | |||||
Map<String, String> messages = MESSAGES.get(locale); | |||||
/* if not loaded yet, load it from database */ | |||||
if (messages == null) { | |||||
messages = new HashMap<String, String>(); | |||||
/* convert the list to a simple messages map */ | |||||
List<Map<String, Object>> rawMessages = loadMessages(locale); | |||||
for (Map<String, Object> rm : rawMessages) { | |||||
messages.put((String) rm.get("code"), (String) rm.get("value")); | |||||
} | |||||
/* cache the messages */ | |||||
MESSAGES.put(locale, messages); | |||||
} | |||||
return messages; | |||||
} | |||||
/** | |||||
* Reset the i18n messages cache | |||||
*/ | |||||
public void resetMessagesCache() { | |||||
/* simply create a new map */ | |||||
MESSAGES = new HashMap<String, Map<String, String>>(); | |||||
} | |||||
} |
@@ -0,0 +1,52 @@ | |||||
package com.ffii.core.security.authentication; | |||||
import com.ffii.core.User; | |||||
import org.springframework.security.authentication.AbstractAuthenticationToken; | |||||
/** | |||||
* Authentication Token | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class AuthToken extends AbstractAuthenticationToken { | |||||
private static final long serialVersionUID = 3383254131623375507L; | |||||
private final String token; | |||||
private final User user; | |||||
public AuthToken(String token) { | |||||
super(null); | |||||
this.token = token; | |||||
this.user = null; | |||||
setAuthenticated(false); | |||||
} | |||||
public AuthToken(String token, User user) { | |||||
super(user.getAuthorities()); | |||||
this.token = token; | |||||
this.user = user; | |||||
setAuthenticated(true); | |||||
} | |||||
@Override | |||||
public Object getCredentials() { | |||||
return getToken(); | |||||
} | |||||
@Override | |||||
public Object getPrincipal() { | |||||
return getUser(); | |||||
} | |||||
public String getToken() { | |||||
return token; | |||||
} | |||||
public User getUser() { | |||||
return user; | |||||
} | |||||
} |
@@ -0,0 +1,49 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.security.authentication; | |||||
import com.ffii.core.User; | |||||
import com.ffii.core.security.service.TokenUserDetailsService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.security.authentication.AuthenticationProvider; | |||||
import org.springframework.security.authentication.BadCredentialsException; | |||||
import org.springframework.security.core.Authentication; | |||||
import org.springframework.security.core.AuthenticationException; | |||||
import org.springframework.stereotype.Component; | |||||
/** | |||||
* Token Authentication Provider | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
@Component | |||||
public class TokenAuthenticationProvider implements AuthenticationProvider { | |||||
@Autowired | |||||
private TokenUserDetailsService tokenUserDetailsService; | |||||
@Override | |||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | |||||
final AuthToken authToken = (AuthToken) authentication; | |||||
final String token = authToken.getToken(); | |||||
User user = tokenUserDetailsService.findUserByToken(token); | |||||
if (user == null) | |||||
throw new BadCredentialsException("No user found for token - " + token); | |||||
return new AuthToken(token, user); | |||||
} | |||||
@Override | |||||
public boolean supports(Class<?> authentication) { | |||||
return authentication.equals(AuthToken.class); | |||||
} | |||||
} |
@@ -0,0 +1,74 @@ | |||||
package com.ffii.core.security.filter; | |||||
import java.io.IOException; | |||||
import java.util.Collections; | |||||
import javax.servlet.FilterChain; | |||||
import javax.servlet.ServletException; | |||||
import javax.servlet.ServletRequest; | |||||
import javax.servlet.ServletResponse; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import com.ffii.core.security.authentication.AuthToken; | |||||
import com.ffii.core.utils.StringUtils; | |||||
import org.springframework.security.core.Authentication; | |||||
import org.springframework.security.core.AuthenticationException; | |||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | |||||
import org.springframework.security.web.util.matcher.RequestMatcher; | |||||
/** | |||||
* Authentication Token Filter | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class AuthTokenFilter extends AbstractAuthenticationProcessingFilter { | |||||
public static final String TOKEN_HEADER = "x-auth-token"; | |||||
public AuthTokenFilter(RequestMatcher requestMatcher) { | |||||
super(requestMatcher); | |||||
} | |||||
@Override | |||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |||||
throws IOException, ServletException { | |||||
final String tokenValue = getTokenValue((HttpServletRequest) request); | |||||
// This filter only applies if the header is present | |||||
if (StringUtils.isEmpty(tokenValue)) { | |||||
chain.doFilter(request, response); | |||||
return; | |||||
} | |||||
// On success keep going on the chain | |||||
this.setAuthenticationSuccessHandler((request1, response1, authentication) -> { | |||||
chain.doFilter(request1, response1); | |||||
}); | |||||
super.doFilter(request, response, chain); | |||||
} | |||||
@Override | |||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | |||||
throws AuthenticationException, IOException, ServletException { | |||||
final String tokenValue = getTokenValue(request); | |||||
if (StringUtils.isEmpty(tokenValue)) { | |||||
return null; | |||||
} | |||||
AuthToken token = new AuthToken(tokenValue); | |||||
token.setDetails(authenticationDetailsSource.buildDetails(request)); | |||||
return this.getAuthenticationManager().authenticate(token); | |||||
} | |||||
private String getTokenValue(HttpServletRequest request) { | |||||
return Collections.list(request.getHeaderNames()).stream() | |||||
.filter(header -> TOKEN_HEADER.equalsIgnoreCase(header)).map(header -> request.getHeader(header)) | |||||
.findFirst().orElse(null); | |||||
} | |||||
} |
@@ -0,0 +1,65 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.security.service; | |||||
import java.util.Date; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import com.ffii.core.dao.JdbcDao; | |||||
import com.ffii.core.utils.MapUtils; | |||||
import com.ffii.core.web.AbstractService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import org.springframework.transaction.annotation.Isolation; | |||||
import org.springframework.transaction.annotation.Transactional; | |||||
/** | |||||
* Login Log Service | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
@Service | |||||
public class LoginLogService extends AbstractService { | |||||
@Autowired | |||||
private JdbcDao jdbcDao; | |||||
/** | |||||
* Create Login Log record | |||||
* | |||||
* @param username | |||||
* the username | |||||
* @param remoteAddr | |||||
* request.getRemoteAddr() | |||||
* @param success | |||||
* true if the login is successful, else false | |||||
* | |||||
* @return true if the number of rows inserted is 1 | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = false) | |||||
public boolean createLoginLog(String username, String remoteAddr, boolean success) { | |||||
Map<String, Object> args = new HashMap<>(4); | |||||
args.put("username", username); | |||||
args.put("loginTime", new Date()); | |||||
args.put("ipAddr", remoteAddr); | |||||
args.put("success", success); | |||||
return (jdbcDao.executeInsert("user_login_log", args) == 1); | |||||
} | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public List<Map<String, Object>> listLastLog(String username, int limit) { | |||||
return jdbcDao.queryForList("SELECT success FROM user_login_log where username = :username ORDER BY loginTime DESC LIMIT " + limit, | |||||
MapUtils.toHashMap("username", username)); | |||||
} | |||||
} |
@@ -0,0 +1,143 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.security.service; | |||||
import java.sql.ResultSet; | |||||
import java.sql.SQLException; | |||||
import java.util.ArrayList; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
import com.ffii.core.User; | |||||
import com.ffii.core.dao.JdbcDao; | |||||
import com.ffii.core.utils.MapUtils; | |||||
import com.ffii.core.web.AbstractService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.context.support.MessageSourceAccessor; | |||||
import org.springframework.jdbc.core.RowMapper; | |||||
import org.springframework.security.core.GrantedAuthority; | |||||
import org.springframework.security.core.SpringSecurityMessageSource; | |||||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | |||||
import org.springframework.security.core.userdetails.UserDetails; | |||||
import org.springframework.stereotype.Service; | |||||
import org.springframework.transaction.annotation.Isolation; | |||||
import org.springframework.transaction.annotation.Transactional; | |||||
/** | |||||
* Token User Details Service | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
@Service | |||||
public class TokenUserDetailsService extends AbstractService { | |||||
@Autowired | |||||
private JdbcDao jdbcDao; | |||||
public static final String FIND_USER_BY_DEVICE_TOKEN_SQL = "SELECT u.* FROM users u LEFT JOIN access_token at ON u.id = at.userId" | |||||
+ " WHERE u.deleted = 0 AND at.token = :token"; | |||||
public static final String LOAD_AUTHORITIES_BY_USERNAME_SQL = "SELECT u.username, ua.authority" | |||||
+ " FROM `users_authorities` ua" + " LEFT JOIN `users` u ON ua.userId = u.id" | |||||
+ " WHERE u.deleted = 0 AND u.username = ?"; | |||||
public static final String LOAD_GROUP_AUTHORITIES_BY_USERNAME_SQL = "SELECT g.id, g.name, ga.authority" | |||||
+ " FROM `groups_authorities` ga" + " LEFT JOIN `groups` g ON ga.groupId = g.id AND g.deleted = 0" | |||||
+ " LEFT JOIN `groups_users` gu ON ga.groupId = gu.groupId" + " LEFT JOIN `users` u ON gu.userId = u.id" | |||||
+ " WHERE u.deleted = 0 AND u.username = ?"; | |||||
protected final MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); | |||||
// ~ Constructors | |||||
public TokenUserDetailsService() { | |||||
} | |||||
// ~ Methods | |||||
/** | |||||
* Allows subclasses to add their own granted authorities to the list to be | |||||
* returned in the <tt>UserDetails</tt>. | |||||
* | |||||
* @param username the username, for use by finder methods | |||||
* @param authorities the current granted authorities, as populated from the | |||||
* <code>authoritiesByUsername</code> mapping | |||||
*/ | |||||
protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) { | |||||
// add ROLE_USER for basic access | |||||
authorities.add(new SimpleGrantedAuthority("ROLE_USER")); | |||||
} | |||||
public UserDetails loadUserByToken(String token) { | |||||
User user = findUserByToken(token); // contains no GrantedAuthority[] | |||||
// build GrantedAuthority[] | |||||
Set<GrantedAuthority> authoritiesSet = new HashSet<>(); | |||||
// aadd all user's authorities | |||||
authoritiesSet.addAll(loadUserAuthorities(user.getUsername())); | |||||
// add all user's groups' authorities | |||||
authoritiesSet.addAll(loadGroupAuthorities(user.getUsername())); | |||||
// convert to List | |||||
List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>(authoritiesSet); | |||||
addCustomAuthorities(user.getUsername(), authoritiesList); | |||||
user.setAuthorities(authoritiesList); | |||||
return user; | |||||
} | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public User findUserByToken(String token) { | |||||
List<User> users = jdbcDao.query(FIND_USER_BY_DEVICE_TOKEN_SQL, User.class, MapUtils.toHashMap("token", token), | |||||
1); | |||||
if (users.size() == 1) | |||||
return users.get(0); | |||||
else | |||||
return null; | |||||
} | |||||
/** | |||||
* Loads user authorities by executing SQL | |||||
* | |||||
* @return a list of GrantedAuthority objects for the user | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
protected List<GrantedAuthority> loadUserAuthorities(String username) { | |||||
return jdbcDao.getNamedParameterJdbcTemplate().query(LOAD_AUTHORITIES_BY_USERNAME_SQL, | |||||
MapUtils.toHashMap("username", username), new RowMapper<GrantedAuthority>() { | |||||
@Override | |||||
public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||||
return new SimpleGrantedAuthority(rs.getString("authority")); | |||||
} | |||||
}); | |||||
} | |||||
/** | |||||
* Loads group authorities by executing SQL | |||||
* | |||||
* @return a list of GrantedAuthority objects from the user's groups | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
protected List<GrantedAuthority> loadGroupAuthorities(String username) { | |||||
return jdbcDao.getNamedParameterJdbcTemplate().query(LOAD_GROUP_AUTHORITIES_BY_USERNAME_SQL, | |||||
MapUtils.toHashMap("username", username), new RowMapper<GrantedAuthority>() { | |||||
@Override | |||||
public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||||
return new SimpleGrantedAuthority(rs.getString("authority")); | |||||
} | |||||
}); | |||||
} | |||||
} |
@@ -0,0 +1,239 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.security.userdetails; | |||||
import java.sql.ResultSet; | |||||
import java.sql.SQLException; | |||||
import java.util.ArrayList; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
import com.ffii.core.User; | |||||
import org.springframework.context.ApplicationContextException; | |||||
import org.springframework.context.support.MessageSourceAccessor; | |||||
import org.springframework.jdbc.core.BeanPropertyRowMapper; | |||||
import org.springframework.jdbc.core.RowMapper; | |||||
import org.springframework.jdbc.core.support.JdbcDaoSupport; | |||||
import org.springframework.security.core.GrantedAuthority; | |||||
import org.springframework.security.core.SpringSecurityMessageSource; | |||||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | |||||
import org.springframework.security.core.userdetails.UserDetails; | |||||
import org.springframework.security.core.userdetails.UserDetailsService; | |||||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||||
import org.springframework.util.Assert; | |||||
public class UserDetailsServiceImpl extends JdbcDaoSupport implements UserDetailsService { | |||||
// ~ Static fields/initializers ===================================================================================== | |||||
public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT *" | |||||
+ " FROM `users` WHERE deleted = 0 AND username = ?"; | |||||
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT u.username, ua.authority" | |||||
+ " FROM `users_authorities` ua" | |||||
+ " LEFT JOIN `users` u ON ua.userId = u.id" | |||||
+ " WHERE u.deleted = 0 AND u.username = ?"; | |||||
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY = "SELECT g.id, g.name, ga.authority" | |||||
+ " FROM `groups_authorities` ga" | |||||
+ " LEFT JOIN `groups` g ON ga.groupId = g.id AND g.deleted = 0" | |||||
+ " LEFT JOIN `groups_users` gu ON ga.groupId = gu.groupId" | |||||
+ " LEFT JOIN `users` u ON gu.userId = u.id" | |||||
+ " WHERE u.deleted = 0 AND u.username = ?"; | |||||
// ~ Instance fields ================================================================================================ | |||||
protected final MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); | |||||
private String usersByUsernameQuery; | |||||
private String authoritiesByUsernameQuery; | |||||
private String groupAuthoritiesByUsernameQuery; | |||||
private boolean enableAuthorities = true; | |||||
private boolean enableGroups; | |||||
// ~ Constructors =================================================================================================== | |||||
public UserDetailsServiceImpl() { | |||||
usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY; | |||||
authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY; | |||||
groupAuthoritiesByUsernameQuery = DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY; | |||||
} | |||||
// ~ Methods ======================================================================================================== | |||||
/** | |||||
* Allows subclasses to add their own granted authorities to the list to be returned in the <tt>UserDetails</tt>. | |||||
* | |||||
* @param username | |||||
* the username, for use by finder methods | |||||
* @param authorities | |||||
* the current granted authorities, as populated from the <code>authoritiesByUsername</code> mapping | |||||
*/ | |||||
protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) { | |||||
// add ROLE_USER for basic access | |||||
authorities.add(new SimpleGrantedAuthority("ROLE_USER")); | |||||
} | |||||
public String getUsersByUsernameQuery() { | |||||
return usersByUsernameQuery; | |||||
} | |||||
@Override | |||||
protected void initDao() throws ApplicationContextException { | |||||
Assert.isTrue(enableAuthorities || enableGroups, "Use of either authorities or groups must be enabled"); | |||||
} | |||||
@Override | |||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |||||
List<User> users = loadUsersByUsername(username); | |||||
if (users.size() == 0) { | |||||
logger.debug("Query returned no results for user '" + username + "'"); | |||||
throw new UsernameNotFoundException( | |||||
messages.getMessage("JdbcDaoImpl.notFound", new Object[] { username }, "Username {0} not found")); | |||||
} | |||||
User user = users.get(0); // contains no GrantedAuthority[] | |||||
Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>(); | |||||
if (enableAuthorities) { | |||||
dbAuthsSet.addAll(loadUserAuthorities(user.getUsername())); | |||||
} | |||||
if (enableGroups) { | |||||
dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername())); | |||||
} | |||||
List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet); | |||||
addCustomAuthorities(user.getUsername(), dbAuths); | |||||
if (dbAuths.size() == 0) { | |||||
logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'"); | |||||
throw new UsernameNotFoundException( | |||||
messages.getMessage("USER.noAuthority", new Object[] { username }, "User {0} has no GrantedAuthority")); | |||||
} | |||||
user.setAuthorities(dbAuths); | |||||
return user; | |||||
} | |||||
/** | |||||
* Executes the SQL <tt>usersByUsernameQuery</tt> and returns a list of UserDetails objects. There should normally only be one matching user. | |||||
*/ | |||||
protected List<User> loadUsersByUsername(String username) { | |||||
return getJdbcTemplate().query(usersByUsernameQuery, new String[] { username }, BeanPropertyRowMapper.newInstance(User.class)); | |||||
} | |||||
/** | |||||
* Loads authorities by executing the SQL from <tt>authoritiesByUsernameQuery</tt>. | |||||
* | |||||
* @return a list of GrantedAuthority objects for the user | |||||
*/ | |||||
protected List<GrantedAuthority> loadUserAuthorities(String username) { | |||||
return getJdbcTemplate().query(authoritiesByUsernameQuery, new String[] { username }, new RowMapper<GrantedAuthority>() { | |||||
@Override | |||||
public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||||
return new SimpleGrantedAuthority(rs.getString("authority")); | |||||
} | |||||
}); | |||||
} | |||||
/** | |||||
* Loads authorities by executing the SQL from <tt>groupAuthoritiesByUsernameQuery</tt>. | |||||
* | |||||
* @return a list of GrantedAuthority objects for the user | |||||
*/ | |||||
protected List<GrantedAuthority> loadGroupAuthorities(String username) { | |||||
return getJdbcTemplate().query(groupAuthoritiesByUsernameQuery, new String[] { username }, new RowMapper<GrantedAuthority>() { | |||||
@Override | |||||
public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { | |||||
return new SimpleGrantedAuthority(rs.getString("authority")); | |||||
} | |||||
}); | |||||
} | |||||
/** | |||||
* Allows the default query string used to retrieve authorities based on username to be overridden, if default table or column names need to be changed. The | |||||
* default query is {@link #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped back to the same | |||||
* column names as in the default query. | |||||
* | |||||
* @param queryString | |||||
* The SQL query string to set | |||||
*/ | |||||
public void setAuthoritiesByUsernameQuery(String queryString) { | |||||
authoritiesByUsernameQuery = queryString; | |||||
} | |||||
protected String getAuthoritiesByUsernameQuery() { | |||||
return authoritiesByUsernameQuery; | |||||
} | |||||
/** | |||||
* Allows the default query string used to retrieve group authorities based on username to be overridden, if default table or column names need to be | |||||
* changed. The default query is {@link #DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped | |||||
* back to the same column names as in the default query. | |||||
* | |||||
* @param queryString | |||||
* The SQL query string to set | |||||
*/ | |||||
public void setGroupAuthoritiesByUsernameQuery(String queryString) { | |||||
groupAuthoritiesByUsernameQuery = queryString; | |||||
} | |||||
/** | |||||
* Allows the default query string used to retrieve users based on username to be overridden, if default table or column names need to be changed. The | |||||
* default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped back to the same column | |||||
* names as in the default query. If the 'enabled' column does not exist in the source database, a permanent true value for this column may be returned by | |||||
* using a query similar to | |||||
* | |||||
* <pre> | |||||
* "select username,password,'true' as enabled from users where username = ?" | |||||
* </pre> | |||||
* | |||||
* @param usersByUsernameQueryString | |||||
* The query string to set | |||||
*/ | |||||
public void setUsersByUsernameQuery(String usersByUsernameQueryString) { | |||||
this.usersByUsernameQuery = usersByUsernameQueryString; | |||||
} | |||||
protected boolean getEnableAuthorities() { | |||||
return enableAuthorities; | |||||
} | |||||
/** | |||||
* Enables loading of authorities from the authorities table. Defaults to true | |||||
*/ | |||||
public void setEnableAuthorities(boolean enableAuthorities) { | |||||
this.enableAuthorities = enableAuthorities; | |||||
} | |||||
protected boolean getEnableGroups() { | |||||
return enableGroups; | |||||
} | |||||
/** | |||||
* Enables support for group authorities. Defaults to false | |||||
* | |||||
* @param enableGroups | |||||
*/ | |||||
public void setEnableGroups(boolean enableGroups) { | |||||
this.enableGroups = enableGroups; | |||||
} | |||||
} |
@@ -0,0 +1,100 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.security.web.authentication; | |||||
import java.io.IOException; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import javax.servlet.ServletException; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import org.apache.commons.logging.Log; | |||||
import org.apache.commons.logging.LogFactory; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.security.core.AuthenticationException; | |||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; | |||||
import com.ffii.core.utils.BooleanUtils; | |||||
import com.ffii.core.utils.JsonUtils; | |||||
import com.ffii.core.utils.MapUtils; | |||||
import com.ffii.core.utils.Params; | |||||
import com.ffii.core.utils.StringUtils; | |||||
import com.ffii.core.utils.web.ServletRequestUtils; | |||||
import com.ffii.core.utils.web.ServletResponseUtils; | |||||
import com.ffii.core.web.view.AbstractView; | |||||
import com.ffii.core.security.service.LoginLogService; | |||||
import com.ffii.tbms.user.service.UserService; | |||||
/** | |||||
* <tt>AuthenticationFailureHandler</tt> implementation which return a failure JSON String upon failed authentication. | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class JsonAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { | |||||
/** Continuous fail 5 time, lock user */ | |||||
private final int TIMES = 5; | |||||
protected final Log logger = LogFactory.getLog(getClass()); | |||||
@Autowired | |||||
private UserService userService; | |||||
@Autowired | |||||
private LoginLogService loginLogService; | |||||
/** | |||||
* Returns the failure JSON String to client. | |||||
*/ | |||||
@Override | |||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) | |||||
throws IOException, ServletException { | |||||
logger.info("Authentication Failure: " + exception.getMessage() + " [" + request.getRemoteAddr() + "]"); | |||||
String username = StringUtils.left(ServletRequestUtils.getStringParameter(request, "username"), 32); | |||||
ServletResponseUtils.disableCaching(response); | |||||
response.setContentType(AbstractView.CONTENT_TYPE_JSON); | |||||
response.setCharacterEncoding("UTF-8"); | |||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // set HTTP status to 401 | |||||
boolean locked = false; | |||||
// log failed login | |||||
if (username != null) { | |||||
loginLogService.createLoginLog(username, request.getRemoteAddr(), false); | |||||
// when failed 5 times, lock account | |||||
List<Map<String, Object>> logs = loginLogService.listLastLog(username, TIMES); | |||||
if (logs.size() >= TIMES) { | |||||
boolean needLock = true; | |||||
for (Map<String, Object> log : logs) { | |||||
if (BooleanUtils.isTrue(log.get("success"))) { | |||||
needLock = false; | |||||
break; | |||||
} | |||||
} | |||||
if (needLock) { | |||||
locked = true; | |||||
userService.lockUser(username, true); | |||||
} | |||||
} | |||||
} | |||||
Map<String, Object> result = MapUtils.toHashMap( | |||||
Params.SUCCESS, Boolean.FALSE, | |||||
Params.MSG, | |||||
locked ? "Account locked (" + TIMES + " Times Failure), please contact your IT administrator." : "Invalid Username or Password."); | |||||
ServletResponseUtils.writeStringToStream(response, AbstractView.CHARSET_UTF8, JsonUtils.toJsonString(result)); | |||||
} | |||||
} |
@@ -0,0 +1,119 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.security.web.authentication; | |||||
import java.io.IOException; | |||||
import java.io.UnsupportedEncodingException; | |||||
import java.net.URLEncoder; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import javax.servlet.ServletException; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import com.ffii.core.Session; | |||||
import com.ffii.core.Settings; | |||||
import com.ffii.core.security.service.LoginLogService; | |||||
import com.ffii.core.setting.service.SettingsService; | |||||
import com.ffii.core.utils.JsonUtils; | |||||
import com.ffii.core.utils.LocaleUtils; | |||||
import com.ffii.core.utils.Params; | |||||
import com.ffii.core.utils.SecurityUtils; | |||||
import com.ffii.core.utils.web.ServletResponseUtils; | |||||
import com.ffii.core.web.view.AbstractView; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.http.MediaType; | |||||
import org.springframework.security.core.Authentication; | |||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; | |||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache; | |||||
import org.springframework.security.web.savedrequest.RequestCache; | |||||
import org.springframework.security.web.savedrequest.SavedRequest; | |||||
import org.springframework.util.StringUtils; | |||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver; | |||||
import org.springframework.web.util.WebUtils; | |||||
/** | |||||
* <tt>AuthenticationSuccessHandler</tt> implementation which return a success JSON String upon successful authentication. | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class JsonAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { | |||||
private RequestCache requestCache = new HttpSessionRequestCache(); | |||||
@Autowired | |||||
private LoginLogService loginLogService; | |||||
@Autowired | |||||
private SettingsService settingsService; | |||||
/** | |||||
* Returns the successful JSON String to client, unless the account is locked. | |||||
*/ | |||||
@Override | |||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) | |||||
throws IOException, ServletException { | |||||
logger.info("Authentication Success: " + authentication.getName() + " [" + request.getRemoteAddr() + "]"); | |||||
ServletResponseUtils.disableCaching(response); | |||||
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); | |||||
response.setCharacterEncoding("UTF-8"); | |||||
// save available locales to session | |||||
String[] availableLocales = settingsService.getString(Settings.SYS_AVAILABLE_LOCALES).split(","); | |||||
Session.setAttribute(request, Session.AVAILABLE_LOCALES, availableLocales); | |||||
// set user's default locale | |||||
String locale = SecurityUtils.getUser().getLocale(); | |||||
if (!StringUtils.isEmpty(locale)) | |||||
WebUtils.setSessionAttribute(request, SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, LocaleUtils.toLocale(locale)); | |||||
// extra values to be passed to client | |||||
Map<String, Object> values = new HashMap<String, Object>(); | |||||
SavedRequest savedRequest = requestCache.getRequest(request, response); | |||||
if (savedRequest != null) { | |||||
String targetUrlParameter = getTargetUrlParameter(); | |||||
if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) { | |||||
requestCache.removeRequest(request, response); | |||||
authenticationSuccess(request, response, null); | |||||
return; | |||||
} | |||||
// Use the DefaultSavedRequest URL | |||||
String targetUrl = savedRequest.getRedirectUrl(); | |||||
logger.debug("Sending DefaultSavedRequest Url: " + targetUrl); | |||||
// target URL to redirect to (if any) | |||||
values.put("targetUrl", URLEncoder.encode(targetUrl, "UTF-8")); | |||||
} | |||||
authenticationSuccess(request, response, values); | |||||
} | |||||
protected void authenticationSuccess(HttpServletRequest request, HttpServletResponse response, Map<String, Object> values) | |||||
throws UnsupportedEncodingException, IOException { | |||||
Map<String, Object> args = new HashMap<String, Object>(); | |||||
args.put(Params.SUCCESS, Boolean.TRUE); | |||||
if (values != null) args.putAll(values); | |||||
clearAuthenticationAttributes(request); | |||||
ServletResponseUtils.writeStringToStream(response, AbstractView.CHARSET_UTF8, JsonUtils.toJsonString(args)); | |||||
// log successful login | |||||
loginLogService.createLoginLog(SecurityUtils.getUser().getUsername(), request.getRemoteAddr(), true); | |||||
} | |||||
} |
@@ -0,0 +1,64 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.setting; | |||||
import java.io.Serializable; | |||||
public class Setting implements Serializable { | |||||
private static final long serialVersionUID = 3925955875238868120L; | |||||
private String category; | |||||
private String type; | |||||
private String name; | |||||
private String value; | |||||
/** default constructor */ | |||||
public Setting() { | |||||
} | |||||
public String getCategory() { | |||||
return category; | |||||
} | |||||
public void setCategory(String category) { | |||||
this.category = category; | |||||
} | |||||
public String getType() { | |||||
return type; | |||||
} | |||||
public void setType(String type) { | |||||
this.type = type; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getValue() { | |||||
return value; | |||||
} | |||||
public void setValue(String value) { | |||||
this.value = value; | |||||
} | |||||
} |
@@ -0,0 +1,169 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.setting.service; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import org.springframework.transaction.annotation.Isolation; | |||||
import org.springframework.transaction.annotation.Transactional; | |||||
import com.ffii.core.dao.JdbcDao; | |||||
import com.ffii.core.setting.Setting; | |||||
import com.ffii.core.utils.NumberUtils; | |||||
import com.ffii.core.utils.Params; | |||||
/** | |||||
* System Settings Service | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
@Service | |||||
public class SettingsService { | |||||
private static final String SQL_LOAD_SETTINGS = "SELECT * FROM settings ORDER BY category, name"; | |||||
private static final String SQL_INSERT_OR_UPDATE_SETTING = "INSERT INTO settings (`name`, `value`) VALUES (:name, :value) ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `value` = VALUES(`value`)"; | |||||
/** | |||||
* Settings cache | |||||
*/ | |||||
private static Map<String, Setting> SETTINGS = null; | |||||
@Autowired | |||||
private JdbcDao jdbcDao; | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public List<Map<String, Object>> searchSettings(Map<String, Object> args) { | |||||
StringBuilder sql = new StringBuilder("SELECT * FROM settings WHERE 1=1"); | |||||
if (args.containsKey("category")) sql.append(" AND category = :category"); | |||||
if (args.containsKey("type")) sql.append(" AND type = :type"); | |||||
if (args.containsKey("name")) sql.append(" AND name = :name"); | |||||
sql.append(" ORDER BY category, name"); | |||||
return jdbcDao.queryForList(sql.toString(), args); | |||||
} | |||||
/** | |||||
* Load and return a Map of all system settings from database | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public Map<String, Setting> loadSettingsMap() { | |||||
List<Setting> settings = jdbcDao.query(SQL_LOAD_SETTINGS, Setting.class, null, 0); | |||||
Map<String, Setting> settingsMap = new HashMap<String, Setting>(); | |||||
for (Setting setting : settings) { | |||||
settingsMap.put(setting.getName(), setting); | |||||
} | |||||
return settingsMap; | |||||
} | |||||
/** | |||||
* @param name | |||||
* the name of the setting | |||||
* @param defaultValue | |||||
* the default value to return if setting is not found | |||||
* | |||||
* @return the String value of the setting, or the default value if not found | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public String getString(String name, String defaultValue) { | |||||
if (SETTINGS == null) SETTINGS = loadSettingsMap(); | |||||
return SETTINGS.get(name) != null ? SETTINGS.get(name).getValue() : defaultValue; | |||||
} | |||||
/** | |||||
* @param name | |||||
* the name of the setting | |||||
* | |||||
* @return the String value of the setting, or {@code null} if not found | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public String getString(String name) { | |||||
return getString(name, null); | |||||
} | |||||
/** | |||||
* @param name | |||||
* the name of the setting | |||||
* @param defaultValue | |||||
* the default value to return if setting is not found | |||||
* | |||||
* @return the int value of the setting, or the default value if not found | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public int getInt(String name, int defaultValue) { | |||||
return NumberUtils.toInt(getString(name), defaultValue); | |||||
} | |||||
/** | |||||
* @param name | |||||
* the name of the setting | |||||
* | |||||
* @return the int value of the setting, or zero (0) if not found | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public int getInt(String name) { | |||||
return getInt(name, 0); | |||||
} | |||||
/** | |||||
* @param name | |||||
* the name of the setting | |||||
* @param defaultValue | |||||
* the default value to return if setting is not found | |||||
* | |||||
* @return the double value of the setting, or the default value if not found | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public double getDouble(String name, double defaultValue) { | |||||
return NumberUtils.toDouble(getString(name), defaultValue); | |||||
} | |||||
/** | |||||
* @param name | |||||
* the name of the setting | |||||
* | |||||
* @return the double value of the setting, or zero (0.0) if not found | |||||
*/ | |||||
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) | |||||
public double getDouble(String name) { | |||||
return getDouble(name, 0.0d); | |||||
} | |||||
/** | |||||
* @param name | |||||
* the name of the setting | |||||
* @param value | |||||
* the value of the setting | |||||
*/ | |||||
@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false) | |||||
public int saveSetting(String name, String value) { | |||||
Map<String, Object> args = new HashMap<String, Object>(2); | |||||
args.put(Params.NAME, name); | |||||
args.put(Params.VALUE, value); | |||||
return jdbcDao.executeUpdate(SQL_INSERT_OR_UPDATE_SETTING, args); | |||||
} | |||||
/** | |||||
* Reset the settings cache | |||||
*/ | |||||
public void resetSettingsCache() { | |||||
// simply set the settings cache to null | |||||
SETTINGS = null; | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.support; | |||||
import java.beans.PropertyEditorSupport; | |||||
import java.util.Date; | |||||
import org.springframework.util.StringUtils; | |||||
import com.ffii.core.utils.DateUtils; | |||||
/** | |||||
* Property Editor for Date in the format defined by {@link DateUtils#PARSE_PATTERNS}<br> | |||||
* <p> | |||||
* Defaults to <code>null</code> if cannot be parsed, never throw an Exception | |||||
* </p> | |||||
* | |||||
* @see DateUtils#parseDateStrictly(String, String[], Date) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SimpleDateEditor extends PropertyEditorSupport { | |||||
@Override | |||||
public void setAsText(String text) throws IllegalArgumentException { | |||||
if (StringUtils.hasText(text)) | |||||
setValue(DateUtils.parseDateStrictly(text, DateUtils.PARSE_PATTERNS, null)); | |||||
else | |||||
setValue(null); | |||||
} | |||||
@Override | |||||
public String getAsText() { | |||||
return DateUtils.formatDate((Date) getValue()); | |||||
} | |||||
} |
@@ -0,0 +1,38 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.support; | |||||
import java.beans.PropertyEditorSupport; | |||||
import java.math.BigDecimal; | |||||
import com.ffii.core.utils.NumberUtils; | |||||
import com.ffii.core.utils.StringUtils; | |||||
/** | |||||
* Property Editor for BigDecimal (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||||
* | |||||
* @see NumberUtils#toDecimal(String, BigDecimal) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SimpleDecimalEditor extends PropertyEditorSupport { | |||||
@Override | |||||
public void setAsText(String value) throws IllegalArgumentException { | |||||
setValue(NumberUtils.toDecimal(value, null)); | |||||
} | |||||
@Override | |||||
public String getAsText() { | |||||
return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.support; | |||||
import java.beans.PropertyEditorSupport; | |||||
import com.ffii.core.utils.NumberUtils; | |||||
import com.ffii.core.utils.StringUtils; | |||||
/** | |||||
* Property Editor for Double (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||||
* | |||||
* @see NumberUtils#toDouble(String, Double) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SimpleDoubleEditor extends PropertyEditorSupport { | |||||
@Override | |||||
public void setAsText(String value) throws IllegalArgumentException { | |||||
setValue(NumberUtils.toDouble(value, null)); | |||||
} | |||||
@Override | |||||
public String getAsText() { | |||||
return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.support; | |||||
import java.beans.PropertyEditorSupport; | |||||
import com.ffii.core.utils.NumberUtils; | |||||
import com.ffii.core.utils.StringUtils; | |||||
/** | |||||
* Property Editor for Integer (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||||
* | |||||
* @see NumberUtils#toInt(String, Integer) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SimpleIntegerEditor extends PropertyEditorSupport { | |||||
@Override | |||||
public void setAsText(String value) throws IllegalArgumentException { | |||||
setValue(NumberUtils.toInt(value, null)); | |||||
} | |||||
@Override | |||||
public String getAsText() { | |||||
return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.support; | |||||
import java.beans.PropertyEditorSupport; | |||||
import com.ffii.core.utils.NumberUtils; | |||||
import com.ffii.core.utils.StringUtils; | |||||
/** | |||||
* Property Editor for Long (Defaults to <code>null</code> if cannot be parsed, never throw an Exception) | |||||
* | |||||
* @see NumberUtils#toLong(String, Long) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SimpleLongEditor extends PropertyEditorSupport { | |||||
@Override | |||||
public void setAsText(String value) throws IllegalArgumentException { | |||||
setValue(NumberUtils.toLong(value, null)); | |||||
} | |||||
@Override | |||||
public String getAsText() { | |||||
return (getValue() == null ? StringUtils.EMPTY : getValue().toString()); | |||||
} | |||||
} |
@@ -0,0 +1,41 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.support; | |||||
import java.beans.PropertyEditorSupport; | |||||
import com.ffii.core.utils.StringUtils; | |||||
/** | |||||
* <p> | |||||
* Property Editor for String | |||||
* </p> | |||||
* <p> | |||||
* Use {@link StringUtils.trimToEmpty()} when binding the value and getting the value to display | |||||
* </p> | |||||
* | |||||
* @see StringUtils#trimToEmpty(String) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SimpleStringTrimToEmptyEditor extends PropertyEditorSupport { | |||||
@Override | |||||
public void setAsText(String value) throws IllegalArgumentException { | |||||
setValue(StringUtils.trimToEmpty(value)); | |||||
} | |||||
@Override | |||||
public String getAsText() { | |||||
return (getValue() == null ? StringUtils.EMPTY : StringUtils.trimToEmpty((String) getValue())); | |||||
} | |||||
} |
@@ -0,0 +1,42 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.support; | |||||
import java.beans.PropertyEditorSupport; | |||||
import com.ffii.core.utils.StringUtils; | |||||
/** | |||||
* <p> | |||||
* Property Editor for String | |||||
* </p> | |||||
* <p> | |||||
* Use {@link StringUtils.trimToNull()} when binding the value, but use {@link StringUtils.trimToEmpty()} when getting the value to display | |||||
* </p> | |||||
* | |||||
* @see StringUtils#trimToNull(String) | |||||
* @see StringUtils#trimToEmpty(String) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SimpleStringTrimToNullEditor extends PropertyEditorSupport { | |||||
@Override | |||||
public void setAsText(String value) throws IllegalArgumentException { | |||||
setValue(StringUtils.trimToNull(value)); | |||||
} | |||||
@Override | |||||
public String getAsText() { | |||||
return (getValue() == null ? StringUtils.EMPTY : StringUtils.trimToEmpty((String) getValue())); | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
package com.ffii.core.utils; | |||||
import java.util.Map; | |||||
/** | |||||
* @author fung | |||||
*/ | |||||
public class ArgsBuilder extends MapBuilder<String, Object> { | |||||
public ArgsBuilder() { | |||||
} | |||||
public ArgsBuilder(Map<String, Object> args) { | |||||
super(args); | |||||
} | |||||
} |
@@ -0,0 +1,67 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
public class BooleanUtils extends org.apache.commons.lang3.BooleanUtils { | |||||
/** | |||||
* <p> | |||||
* For the case of {@code Number}, only if the value is {@code 1} will return {@code true}. Otherwise, {@code false} is returned. | |||||
* </p> | |||||
* <p> | |||||
* For the case of {@code String}, {@code 'true'}, {@code 'on'} or {@code 'yes'} (case insensitive) will return {@code true}. Otherwise, {@code false} is | |||||
* returned. | |||||
* </p> | |||||
* <p> | |||||
* For the case of {@code Boolean}, only if the {@code Boolean} value is {@code true} will return {@code true}, handling {@code null} by returning | |||||
* {@code false}. | |||||
* </p> | |||||
* <p> | |||||
* For any other cases including {@code null} will return {@code false}. | |||||
* </p> | |||||
*/ | |||||
public static boolean isTrue(Object obj) { | |||||
if (obj instanceof Number) { | |||||
return ((Number) obj).intValue() == 1; | |||||
} else if (obj instanceof String) { | |||||
return toBoolean((String) obj); | |||||
} else { | |||||
return Boolean.TRUE.equals(obj); | |||||
} | |||||
} | |||||
/** | |||||
* <p> | |||||
* For the case of {@code Number}, only if the value is {@code 0} will return {@code true}. Otherwise, {@code false} is returned. | |||||
* </p> | |||||
* <p> | |||||
* For the case of {@code String}, {@code true} is returned unless the value is {@code 'true'}, {@code 'on'} or {@code 'yes'} (case insensitive) which will | |||||
* return {@code false}. | |||||
* </p> | |||||
* <p> | |||||
* For the case of {@code Boolean}, only if the {@code Boolean} value is {@code false} will return {@code true}, handling {@code null} by returning | |||||
* {@code false}. | |||||
* </p> | |||||
* <p> | |||||
* For any other cases including {@code null} will return {@code false}. | |||||
* </p> | |||||
*/ | |||||
public static boolean isFalse(Object obj) { | |||||
if (obj instanceof Number) { | |||||
return ((Number) obj).intValue() == 0; | |||||
} else if (obj instanceof String) { | |||||
return !toBoolean((String) obj); | |||||
} else { | |||||
return Boolean.FALSE.equals(obj); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,66 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
/** | |||||
* CheckDigitUtils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class CheckDigitUtils { | |||||
/* | |||||
* ISO/IEC 7812-1 Annex B | |||||
* Check digit calc method | |||||
*/ | |||||
public static int luhnCalc(String digitsString) { | |||||
int sum = 0; | |||||
boolean alternate = false; | |||||
for (int i = 0; i < digitsString.length(); i++) { | |||||
int n = Integer.parseInt(digitsString.substring(i, i + 1)); | |||||
if (alternate) { | |||||
n *= 2; | |||||
if (n > 9) { | |||||
n = (n % 10) + 1; | |||||
} | |||||
} | |||||
sum += n; | |||||
alternate = !alternate; | |||||
} | |||||
return ((sum * 9) % 10); | |||||
} | |||||
/* | |||||
* ISO/IEC 7812-1 Annex B | |||||
* Check digit check method | |||||
*/ | |||||
public static boolean luhnCheck(String digitsStringWithCheckDigit) { | |||||
int sum = 0; | |||||
boolean alternate = false; | |||||
for (int i = digitsStringWithCheckDigit.length() - 1; i >= 0; i--) { | |||||
try { | |||||
int n = Integer.parseInt(digitsStringWithCheckDigit.substring(i, i + 1)); | |||||
if (alternate) { | |||||
n *= 2; | |||||
if (n > 9) { | |||||
n = (n % 10) + 1; | |||||
} | |||||
} | |||||
sum += n; | |||||
alternate = !alternate; | |||||
} catch (NumberFormatException nfe) { | |||||
continue; | |||||
} | |||||
} | |||||
return (sum % 10 == 0); | |||||
} | |||||
} |
@@ -0,0 +1,119 @@ | |||||
package com.ffii.core.utils; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import org.springframework.web.bind.ServletRequestBindingException; | |||||
/** | |||||
* ArgsBuilder of Criteria | |||||
* | |||||
* @see {@link CriteriaUtils} | |||||
* @author fung | |||||
*/ | |||||
public class CriteriaArgsBuilder { | |||||
private Map<String, Object> args; | |||||
private HttpServletRequest request; | |||||
public CriteriaArgsBuilder(HttpServletRequest request) { | |||||
args = new HashMap<String, Object>(); | |||||
this.request = request; | |||||
} | |||||
public CriteriaArgsBuilder(HttpServletRequest request, Map<String, Object> args) { | |||||
this.args = args; | |||||
this.request = request; | |||||
} | |||||
public CriteriaArgsBuilder addStringExact(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addStringExact(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addStringLike(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addStringLike(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addString(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addString(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addStringContains(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addStringContains(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addStringStartsWith(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addStringStartsWith(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addStringEndsWith(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addStringEndsWith(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addStringList(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addStringList(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addStringCsv(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addStringCsv(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addInteger(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addInteger(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addNonZeroInteger(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addNonZeroInteger(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addIntegerList(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addIntegerList(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addNonZeroIntegerList(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addNonZeroIntegerList(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addLong(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addLong(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addNonZeroLong(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addNonZeroLong(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addDate(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addDate(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addDateTo(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addDateTo(request, args, paramName); | |||||
return this; | |||||
} | |||||
public CriteriaArgsBuilder addBoolean(String paramName) throws ServletRequestBindingException { | |||||
CriteriaUtils.addBoolean(request, args, paramName); | |||||
return this; | |||||
} | |||||
public Map<String, Object> toMap() { | |||||
return this.args; | |||||
} | |||||
} |
@@ -0,0 +1,191 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.sql.Date; | |||||
import java.util.ArrayList; | |||||
import java.util.Calendar; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import org.springframework.web.bind.ServletRequestBindingException; | |||||
import com.ffii.core.utils.web.ServletRequestUtils; | |||||
/** | |||||
* Utils for getting parameter values from HTTP Request and put them into a criteria map if found | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class CriteriaUtils { | |||||
/** | |||||
* Alias for {@link #addString(HttpServletRequest, Map, String)} | |||||
*/ | |||||
public static void addStringExact(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
addString(request, args, paramName); | |||||
} | |||||
/** | |||||
* Alias for {@link #addStringContains(HttpServletRequest, Map, String)} | |||||
*/ | |||||
public static void addStringLike(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
addStringContains(request, args, paramName); | |||||
} | |||||
/** | |||||
* Add a String (an exact value for using equal sign in SQL) parameter to criteria. Usually used for exact string search. | |||||
*/ | |||||
public static void addString(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||||
if (value != null) args.put(paramName, value); | |||||
} | |||||
/** | |||||
* Add a String (a substring value for using <code>LIKE</code> in SQL) parameter to criteria. Usually used for substring search for values that contains the | |||||
* input value. | |||||
*/ | |||||
public static void addStringContains(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||||
if (value != null) args.put(paramName, StringUtils.PERCENT + value + StringUtils.PERCENT); | |||||
} | |||||
/** | |||||
* Add a String (a substring value for using <code>LIKE</code> in SQL) parameter to criteria. Usually used for substring search for values that starts with | |||||
* the input value. | |||||
*/ | |||||
public static void addStringStartsWith(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||||
if (value != null) args.put(paramName, value + StringUtils.PERCENT); | |||||
} | |||||
/** | |||||
* Add a String (a substring value for using <code>LIKE</code> in SQL) parameter to criteria. Usually used for substring search for values that ends with | |||||
* the input value. | |||||
*/ | |||||
public static void addStringEndsWith(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
String value = ServletRequestUtils.getTrimmedStringParameter(request, paramName); | |||||
if (value != null) args.put(paramName, StringUtils.PERCENT + value); | |||||
} | |||||
/** | |||||
* Add String List parameter to criteria. | |||||
*/ | |||||
public static void addStringList(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
String[] params = ServletRequestUtils.getStringParameters(request, paramName); | |||||
if (params.length > 0) { | |||||
List<String> value = new ArrayList<String>(); | |||||
for (int i = 0; i < params.length; i++) | |||||
if (StringUtils.isNotBlank(params[i])) value.add(params[i]); | |||||
if (value.size() > 0) args.put(paramName, value); | |||||
} | |||||
} | |||||
/** | |||||
* Add String CSV parameter to criteria. | |||||
*/ | |||||
public static void addStringCsv(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
String text = ServletRequestUtils.getStringParameter(request, paramName); | |||||
if (StringUtils.isNotEmpty(text)) { | |||||
String[] params = text.split(","); | |||||
List<String> values = new ArrayList<String>(); | |||||
for (int i = 0; i < params.length; i++) | |||||
values.add(params[i]); | |||||
args.put(paramName, values); | |||||
} | |||||
} | |||||
/** | |||||
* Add an Integer parameter to criteria. | |||||
*/ | |||||
public static void addInteger(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
Integer value = ServletRequestUtils.getIntParameter(request, paramName, null); | |||||
if (value != null) args.put(paramName, value); | |||||
} | |||||
/** | |||||
* Add a non-zero Integer parameter to criteria. | |||||
*/ | |||||
public static void addNonZeroInteger(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
Integer value = ServletRequestUtils.getIntParameter(request, paramName, null); | |||||
if (value != null && value.intValue() != 0) args.put(paramName, value); | |||||
} | |||||
/** | |||||
* Add Integer List parameter to criteria. | |||||
*/ | |||||
public static void addIntegerList(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
int[] params = ServletRequestUtils.getIntParameters(request, paramName); | |||||
if (params.length > 0) { | |||||
List<Integer> values = new ArrayList<Integer>(); | |||||
for (int i = 0; i < params.length; i++) | |||||
values.add(params[i]); | |||||
args.put(paramName, values); | |||||
} | |||||
} | |||||
/** | |||||
* Add non-zero Integer List parameter to criteria. | |||||
*/ | |||||
public static void addNonZeroIntegerList(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
int[] params = ServletRequestUtils.getIntParameters(request, paramName); | |||||
if (params.length > 0) { | |||||
List<Integer> values = new ArrayList<Integer>(); | |||||
for (int i = 0; i < params.length; i++) | |||||
if (params[i] != 0) values.add(params[i]); | |||||
args.put(paramName, values); | |||||
} | |||||
} | |||||
/** | |||||
* Add a Long parameter to criteria. | |||||
*/ | |||||
public static void addLong(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
Long value = ServletRequestUtils.getLongParameter(request, paramName, null); | |||||
if (value != null) args.put(paramName, value); | |||||
} | |||||
/** | |||||
* Add a non-zero Long parameter to criteria. | |||||
*/ | |||||
public static void addNonZeroLong(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
Long value = ServletRequestUtils.getLongParameter(request, paramName, null); | |||||
if (value != null && value.longValue() != 0L) args.put(paramName, value); | |||||
} | |||||
/** | |||||
* Add a SQL Date parameter to criteria. Usually used for <code>FROM</code> date range search, or exact date search. | |||||
*/ | |||||
public static void addDate(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
Date value = ServletRequestUtils.getSqlDateParameter(request, paramName); | |||||
if (value != null) args.put(paramName, value); | |||||
} | |||||
/** | |||||
* Add a SQL Date (plus 1 day) parameter to criteria. Usually used for {@code TO} date range search. | |||||
*/ | |||||
public static void addDateTo(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
Date value = ServletRequestUtils.getSqlDateParameter(request, paramName); | |||||
if (value != null) args.put(paramName, DateUtils.add(value.getClass(), value, Calendar.DAY_OF_MONTH, 1)); | |||||
} | |||||
/** | |||||
* Add a Boolean parameter to criteria if it's not {@code null}. Accepts "true", "on", "yes" (any case) and "1" as values for true; treats every other | |||||
* non-empty value as false (i.e. parses leniently). | |||||
*/ | |||||
public static void addBoolean(HttpServletRequest request, Map<String, Object> args, String paramName) throws ServletRequestBindingException { | |||||
Boolean value = ServletRequestUtils.getBooleanParameter(request, paramName); | |||||
if (value != null) args.put(paramName, value); | |||||
} | |||||
} |
@@ -0,0 +1,103 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.util.Map; | |||||
import org.apache.commons.lang3.ArrayUtils; | |||||
import org.springframework.beans.MutablePropertyValues; | |||||
import org.springframework.web.bind.WebDataBinder; | |||||
import com.ffii.core.support.SimpleDateEditor; | |||||
import com.ffii.core.support.SimpleIntegerEditor; | |||||
import com.ffii.core.support.SimpleStringTrimToNullEditor; | |||||
/** | |||||
* DataBindUtils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class DataBindUtils { | |||||
/** coreFields */ | |||||
public static final String[] coreFields = { Params.ID, "ownerId", "deleted", "createdBy", "modifiedBy", "created", "modified", "password" }; | |||||
/** | |||||
* | |||||
* @param object | |||||
* @return WebDataBinder | |||||
*/ | |||||
private static WebDataBinder createBinder(Object object) { | |||||
WebDataBinder binder = new WebDataBinder(object); | |||||
binder.registerCustomEditor(String.class, new SimpleStringTrimToNullEditor()); | |||||
binder.registerCustomEditor(Integer.class, new SimpleIntegerEditor()); | |||||
binder.registerCustomEditor(java.util.Date.class, new SimpleDateEditor()); | |||||
return binder; | |||||
} | |||||
/** | |||||
* Binds data using {@link WebDataBinder} with the following custom editors:- | |||||
* | |||||
* <ul> | |||||
* <li>{@link SimpleStringTrimToNullEditor}</li> | |||||
* <li>{@link SimpleIntegerEditor}</li> | |||||
* <li>{@link SimpleDateEditor}</li> | |||||
* </ul> | |||||
* | |||||
* @param record | |||||
* record Map | |||||
* @param object | |||||
* the target object to bind onto | |||||
* @param disallowFields | |||||
* optional | |||||
*/ | |||||
public static void bindRecord(Map<String, Object> record, Object object, String... disallowFields) { | |||||
WebDataBinder binder = createBinder(object); | |||||
binder.setDisallowedFields(disallowFields); | |||||
binder.bind(new MutablePropertyValues(record)); | |||||
}; | |||||
/** | |||||
* Binds data using {@link WebDataBinder} with the following custom editors:- | |||||
* | |||||
* <ul> | |||||
* <li>{@link SimpleStringTrimToNullEditor}</li> | |||||
* <li>{@link SimpleIntegerEditor}</li> | |||||
* <li>{@link SimpleDateEditor}</li> | |||||
* </ul> | |||||
* | |||||
* <p> | |||||
* <b>Important:</b> The following system fields will NOT be binded for security reasons. | |||||
* <ul> | |||||
* <li>id</li> | |||||
* <li>ownerId</li> | |||||
* <li>deleted</li> | |||||
* <li>createdBy</li> | |||||
* <li>modifiedBy</li> | |||||
* <li>created</li> | |||||
* <li>modified</li> | |||||
* <li>password</li> | |||||
* </ul> | |||||
* </p> | |||||
* | |||||
* @param record | |||||
* record Map | |||||
* @param object | |||||
* the target object to bind onto | |||||
* @param disallowFields | |||||
* optional | |||||
*/ | |||||
public static void bindRecordWithoutCore(Map<String, Object> record, Object object, String... disallowFields) { | |||||
WebDataBinder binder = createBinder(object); | |||||
binder.setDisallowedFields(ArrayUtils.addAll(coreFields, disallowFields)); | |||||
binder.bind(new MutablePropertyValues(record)); | |||||
}; | |||||
} |
@@ -0,0 +1,139 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2012 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
public class Encoding { | |||||
// Supported Encoding Types | |||||
public static final int GB2312 = 0; | |||||
public static final int GBK = 1; | |||||
public static final int GB18030 = 2; | |||||
public static final int HZ = 3; | |||||
public static final int BIG5 = 4; | |||||
public static final int CNS11643 = 5; | |||||
public static final int UTF8 = 6; | |||||
public static final int UTF8T = 7; | |||||
public static final int UTF8S = 8; | |||||
public static final int UNICODE = 9; | |||||
public static final int UNICODET = 10; | |||||
public static final int UNICODES = 11; | |||||
public static final int ISO2022CN = 12; | |||||
public static final int ISO2022CN_CNS = 13; | |||||
public static final int ISO2022CN_GB = 14; | |||||
public static final int EUC_KR = 15; | |||||
public static final int CP949 = 16; | |||||
public static final int ISO2022KR = 17; | |||||
public static final int JOHAB = 18; | |||||
public static final int SJIS = 19; | |||||
public static final int EUC_JP = 20; | |||||
public static final int ISO2022JP = 21; | |||||
public static final int ASCII = 22; | |||||
public static final int OTHER = 23; | |||||
public static final int TOTALTYPES = 24; | |||||
// Names of the encodings as understood by Java | |||||
public static String[] javaname; | |||||
// Names of the encodings for human viewing | |||||
public static String[] nicename; | |||||
// Names of charsets as used in charset parameter of HTML Meta tag | |||||
public static String[] htmlname; | |||||
static { | |||||
javaname = new String[TOTALTYPES]; | |||||
nicename = new String[TOTALTYPES]; | |||||
htmlname = new String[TOTALTYPES]; | |||||
// Assign encoding names | |||||
javaname[GB2312] = "GB2312"; | |||||
javaname[GBK] = "GBK"; | |||||
javaname[GB18030] = "GB18030"; | |||||
javaname[HZ] = "ASCII"; // What to put here? Sun doesn't support HZ | |||||
javaname[ISO2022CN_GB] = "ISO2022CN_GB"; | |||||
javaname[BIG5] = "BIG5"; | |||||
javaname[CNS11643] = "EUC-TW"; | |||||
javaname[ISO2022CN_CNS] = "ISO2022CN_CNS"; | |||||
javaname[ISO2022CN] = "ISO2022CN"; | |||||
javaname[UTF8] = "UTF8"; | |||||
javaname[UTF8T] = "UTF8"; | |||||
javaname[UTF8S] = "UTF8"; | |||||
javaname[UNICODE] = "Unicode"; | |||||
javaname[UNICODET] = "Unicode"; | |||||
javaname[UNICODES] = "Unicode"; | |||||
javaname[EUC_KR] = "EUC_KR"; | |||||
javaname[CP949] = "MS949"; | |||||
javaname[ISO2022KR] = "ISO2022KR"; | |||||
javaname[JOHAB] = "Johab"; | |||||
javaname[SJIS] = "SJIS"; | |||||
javaname[EUC_JP] = "EUC_JP"; | |||||
javaname[ISO2022JP] = "ISO2022JP"; | |||||
javaname[ASCII] = "ASCII"; | |||||
javaname[OTHER] = "ISO8859_1"; | |||||
// Assign encoding names | |||||
htmlname[GB2312] = "GB2312"; | |||||
htmlname[GBK] = "GBK"; | |||||
htmlname[GB18030] = "GB18030"; | |||||
htmlname[HZ] = "HZ-GB-2312"; | |||||
htmlname[ISO2022CN_GB] = "ISO-2022-CN-EXT"; | |||||
htmlname[BIG5] = "BIG5"; | |||||
htmlname[CNS11643] = "EUC-TW"; | |||||
htmlname[ISO2022CN_CNS] = "ISO-2022-CN-EXT"; | |||||
htmlname[ISO2022CN] = "ISO-2022-CN"; | |||||
htmlname[UTF8] = "UTF-8"; | |||||
htmlname[UTF8T] = "UTF-8"; | |||||
htmlname[UTF8S] = "UTF-8"; | |||||
htmlname[UNICODE] = "UTF-16"; | |||||
htmlname[UNICODET] = "UTF-16"; | |||||
htmlname[UNICODES] = "UTF-16"; | |||||
htmlname[EUC_KR] = "EUC-KR"; | |||||
htmlname[CP949] = "x-windows-949"; | |||||
htmlname[ISO2022KR] = "ISO-2022-KR"; | |||||
htmlname[JOHAB] = "x-Johab"; | |||||
htmlname[SJIS] = "Shift_JIS"; | |||||
htmlname[EUC_JP] = "EUC-JP"; | |||||
htmlname[ISO2022JP] = "ISO-2022-JP"; | |||||
htmlname[ASCII] = "ASCII"; | |||||
htmlname[OTHER] = "ISO8859-1"; | |||||
// Assign Human readable names | |||||
nicename[GB2312] = "GB-2312"; | |||||
nicename[GBK] = "GBK"; | |||||
nicename[GB18030] = "GB18030"; | |||||
nicename[HZ] = "HZ"; | |||||
nicename[ISO2022CN_GB] = "ISO2022CN-GB"; | |||||
nicename[BIG5] = "Big5"; | |||||
nicename[CNS11643] = "CNS11643"; | |||||
nicename[ISO2022CN_CNS] = "ISO2022CN-CNS"; | |||||
nicename[ISO2022CN] = "ISO2022 CN"; | |||||
nicename[UTF8] = "UTF-8"; | |||||
nicename[UTF8T] = "UTF-8 (Trad)"; | |||||
nicename[UTF8S] = "UTF-8 (Simp)"; | |||||
nicename[UNICODE] = "Unicode"; | |||||
nicename[UNICODET] = "Unicode (Trad)"; | |||||
nicename[UNICODES] = "Unicode (Simp)"; | |||||
nicename[EUC_KR] = "EUC-KR"; | |||||
nicename[CP949] = "CP949"; | |||||
nicename[ISO2022KR] = "ISO 2022 KR"; | |||||
nicename[JOHAB] = "Johab"; | |||||
nicename[SJIS] = "Shift-JIS"; | |||||
nicename[EUC_JP] = "EUC-JP"; | |||||
nicename[ISO2022JP] = "ISO 2022 JP"; | |||||
nicename[ASCII] = "ASCII"; | |||||
nicename[OTHER] = "OTHER"; | |||||
} | |||||
} |
@@ -0,0 +1,675 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.io.ByteArrayInputStream; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.io.OutputStream; | |||||
import java.math.BigDecimal; | |||||
import java.security.GeneralSecurityException; | |||||
import java.util.Calendar; | |||||
import java.util.Date; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||||
import org.apache.poi.poifs.crypt.EncryptionInfo; | |||||
import org.apache.poi.poifs.crypt.EncryptionMode; | |||||
import org.apache.poi.poifs.crypt.Encryptor; | |||||
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.DataFormatter; | |||||
import org.apache.poi.ss.usermodel.DateUtil; | |||||
import org.apache.poi.ss.usermodel.RichTextString; | |||||
import org.apache.poi.ss.usermodel.Row; | |||||
import org.apache.poi.ss.usermodel.Sheet; | |||||
import org.apache.poi.ss.usermodel.Workbook; | |||||
import org.apache.poi.ss.util.CellRangeAddress; | |||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||||
import org.springframework.core.io.Resource; | |||||
import org.springframework.core.io.ResourceLoader; | |||||
/** | |||||
* Excel Utils (for Apache POI 3.15 to 3.17) | |||||
* | |||||
* @author Patrick | |||||
* @version 2018-04-06 | |||||
*/ | |||||
public abstract class ExcelUtils { | |||||
/** | |||||
* static A to Z char array | |||||
*/ | |||||
private static final char[] A2Z = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', | |||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; | |||||
private static final DataFormatter DATA_FORMATTER = new DataFormatter(); | |||||
/** max rows limit of .xls **/ | |||||
public static final int MAX_ROWS = 65536; | |||||
/** max columns limit of .xls **/ | |||||
public static final int MAX_COLS = 256; | |||||
/** | |||||
* Column reference to index (0-based) map, support up to 256 columns | |||||
* (compatible with .xls format) | |||||
*/ | |||||
public static final Map<String, Integer> COL_IDX = new HashMap<String, Integer>(MAX_COLS, 1.0f); | |||||
static { | |||||
for (int columnIndex = 0; columnIndex < MAX_COLS; columnIndex++) { | |||||
int tempColumnCount = columnIndex; | |||||
StringBuilder sb = new StringBuilder(2); | |||||
do { | |||||
sb.insert(0, A2Z[tempColumnCount % 26]); | |||||
tempColumnCount = (tempColumnCount / 26) - 1; | |||||
} while (tempColumnCount >= 0); | |||||
COL_IDX.put(sb.toString(), Integer.valueOf(columnIndex)); | |||||
} | |||||
} | |||||
/** | |||||
* Load XSSF workbook (xlsx file) from template source. | |||||
* | |||||
* @param url the relative path to the template source, e.g. | |||||
* "WEB-INF/excel/exampleReportTemplate.xlsx" | |||||
* | |||||
* @return the workbook, or null if the template file cannot be loaded | |||||
*/ | |||||
public static final Workbook loadXSSFWorkbookFromTemplateSource(ResourceLoader resourceLoader, String url) { | |||||
Resource resource = resourceLoader.getResource(url); | |||||
try { | |||||
return new XSSFWorkbook(resource.getInputStream()); | |||||
} catch (IOException e) { | |||||
return null; | |||||
} | |||||
} | |||||
/** | |||||
* Write the workbook to byte array. | |||||
* | |||||
* @param workbook The Excel workbook (cannot be null) | |||||
* | |||||
* @return the byte[], or null if IO exception occurred | |||||
*/ | |||||
public static final byte[] toByteArray(Workbook workbook) { | |||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||||
try { | |||||
workbook.write(baos); | |||||
} catch (IOException e) { | |||||
return null; | |||||
} | |||||
return baos.toByteArray(); | |||||
} | |||||
/** | |||||
* Check if the cell exists in the given sheet, row and column. | |||||
* | |||||
* @param sheet the Sheet (cannot be null) | |||||
* @param rowIndex 0-based row index | |||||
* @param colIndex 0-based column index | |||||
* | |||||
* @return {@code true} if cell exists, else {@code false} | |||||
*/ | |||||
public static final boolean isCellExists(Sheet sheet, int rowIndex, int colIndex) { | |||||
Row row = sheet.getRow(rowIndex); | |||||
if (row != null) { | |||||
Cell cell = row.getCell(colIndex); | |||||
return cell != null; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Convenient method to obtain the cell in the given sheet, row and column. | |||||
* <p> | |||||
* Creates the row and the cell if not already exist. | |||||
* | |||||
* @param sheet the Sheet (cannot be null) | |||||
* @param rowIndex 0-based row index | |||||
* @param colIndex 0-based column index | |||||
* | |||||
* @return the Cell (never null) | |||||
*/ | |||||
public static final Cell getCell(Sheet sheet, int rowIndex, int colIndex) { | |||||
Row row = sheet.getRow(rowIndex); | |||||
if (row == null) { | |||||
row = sheet.createRow(rowIndex); | |||||
} | |||||
Cell cell = row.getCell(colIndex); | |||||
if (cell == null) { | |||||
cell = row.createCell(colIndex); | |||||
} | |||||
return cell; | |||||
} | |||||
/** | |||||
* Get column index by column reference (support up to 256 columns) | |||||
* | |||||
* @param columnRef column reference such as "A", "B", "AA", "AB"... | |||||
* | |||||
* @return the column index | |||||
* | |||||
* @throws NullPointerException if column reference is invalid or the index | |||||
* exceeds 256 | |||||
*/ | |||||
public static final int getColumnIndex(String columnRef) { | |||||
return COL_IDX.get(columnRef); | |||||
} | |||||
/** | |||||
* Get column reference by column index | |||||
* | |||||
* @param columnIndex 0-based column index | |||||
* | |||||
* @return the column reference such as "A", "B", "AA", "AB"... | |||||
*/ | |||||
public static final String getColumnRef(int columnIndex) { | |||||
StringBuilder sb = new StringBuilder(); | |||||
int tempColumnCount = columnIndex; | |||||
do { | |||||
sb.insert(0, A2Z[tempColumnCount % 26]); | |||||
tempColumnCount = (tempColumnCount / 26) - 1; | |||||
} while (tempColumnCount >= 0); | |||||
return sb.toString(); | |||||
} | |||||
/** | |||||
* Get the Excel Cell Ref String by columnIndex and rowIndex | |||||
* | |||||
* @param columnIndex 0-based column index | |||||
* @param rowIndex 0-based row index | |||||
*/ | |||||
public static final String getCellRefString(int columnIndex, int rowIndex) { | |||||
StringBuilder sb = new StringBuilder(); | |||||
int tempColumnCount = columnIndex; | |||||
do { | |||||
sb.insert(0, A2Z[tempColumnCount % 26]); | |||||
tempColumnCount = (tempColumnCount / 26) - 1; | |||||
} while (tempColumnCount >= 0); | |||||
sb.append(rowIndex + 1); | |||||
return sb.toString(); | |||||
} | |||||
/** | |||||
* Get Cell value as <code>String</code> | |||||
*/ | |||||
public static String getStringValue(Cell cell) { | |||||
if (cell != null && cell.getCellTypeEnum() == CellType.FORMULA) { | |||||
try { | |||||
return cell.getStringCellValue(); | |||||
} catch (Exception e) { | |||||
return ""; | |||||
} | |||||
} | |||||
return DATA_FORMATTER.formatCellValue(cell); | |||||
} | |||||
/** | |||||
* Get Cell value as <code>BigDecimal</code>, with a fallback value | |||||
* <p> | |||||
* Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||||
* | |||||
* @return the <code>BigDecimal</code> value, or the default value if cell is | |||||
* <code>null</code> or cell type is {@link Cell#CELL_TYPE_BLANK} | |||||
*/ | |||||
public static BigDecimal getDecimalValue(Cell cell, BigDecimal defaultValue) { | |||||
if (cell == null || cell.getCellTypeEnum() == CellType.BLANK) | |||||
return defaultValue; | |||||
if (cell.getCellTypeEnum() == CellType.STRING) { | |||||
return NumberUtils.toDecimal(cell.getStringCellValue()); | |||||
} else { | |||||
return BigDecimal.valueOf(cell.getNumericCellValue()); | |||||
} | |||||
} | |||||
/** | |||||
* Get Cell value as <code>BigDecimal</code> | |||||
* <p> | |||||
* Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||||
* | |||||
* @return the <code>BigDecimal</code> value, or <code>BigDecimal.ZERO</code> if | |||||
* cell is <code>null</code> or cell type is | |||||
* {@link Cell#CELL_TYPE_BLANK} | |||||
*/ | |||||
public static BigDecimal getDecimalValue(Cell cell) { | |||||
return getDecimalValue(cell, BigDecimal.ZERO); | |||||
} | |||||
/** | |||||
* Get Cell value as <code>double</code> | |||||
* <p> | |||||
* Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||||
*/ | |||||
public static double getDoubleValue(Cell cell) { | |||||
if (cell == null) | |||||
return 0.0; | |||||
if (cell.getCellTypeEnum() == CellType.STRING) { | |||||
return NumberUtils.toDouble(cell.getStringCellValue()); | |||||
} else { | |||||
return cell.getNumericCellValue(); | |||||
} | |||||
} | |||||
/** | |||||
* Get Cell value as <code>int</code> (rounded half-up to the nearest integer) | |||||
* <p> | |||||
* Only support {@link Cell#CELL_TYPE_NUMERIC} and {@link Cell#CELL_TYPE_STRING} | |||||
*/ | |||||
public static int getIntValue(Cell cell) { | |||||
return (int) NumberUtils.round(getDoubleValue(cell), 0); | |||||
} | |||||
/** | |||||
* Get Cell Integer value (truncated) | |||||
*/ | |||||
public static Integer getIntValue(Cell cell, Integer defaultValue) { | |||||
if (cell == null) | |||||
return defaultValue; | |||||
if (cell.getCellTypeEnum() == CellType.STRING) { | |||||
return NumberUtils.toInt(cell.getStringCellValue(), defaultValue); | |||||
} else { | |||||
return (int) cell.getNumericCellValue(); | |||||
} | |||||
} | |||||
/** | |||||
* Get Cell Date value | |||||
*/ | |||||
public static Date getDateValue(Cell cell) { | |||||
if (cell == null) | |||||
return null; | |||||
if (cell.getCellTypeEnum() == CellType.STRING) { | |||||
return DateUtils.parseDateStrictly(cell.getStringCellValue(), DateUtils.PARSE_PATTERNS, null); | |||||
} | |||||
if (DateUtil.isCellDateFormatted(cell)) { | |||||
try { | |||||
return DateUtil.getJavaDate(cell.getNumericCellValue()); | |||||
} catch (NumberFormatException e) { | |||||
return null; | |||||
} | |||||
} else { | |||||
return null; | |||||
} | |||||
} | |||||
/** | |||||
* Convenient method to set Cell value | |||||
* | |||||
* @param cell the Cell (cannot be null) | |||||
* @param value the value to set | |||||
*/ | |||||
public static void setCellValue(Cell cell, Object value) { | |||||
if (value instanceof String) | |||||
cell.setCellValue((String) value); | |||||
else if (value instanceof RichTextString) | |||||
cell.setCellValue((RichTextString) value); | |||||
else if (value instanceof Number) | |||||
cell.setCellValue(((Number) value).doubleValue()); | |||||
else if (value instanceof Boolean) | |||||
cell.setCellValue(((Boolean) value).booleanValue()); | |||||
else if (value instanceof Calendar) | |||||
cell.setCellValue((Calendar) value); | |||||
else if (value instanceof Date) | |||||
cell.setCellValue((Date) value); | |||||
else if (value == null) | |||||
cell.setCellValue(""); | |||||
else | |||||
throw new IllegalArgumentException(value.getClass().toString() + " is not supported"); | |||||
} | |||||
/** | |||||
* Convenient method to set Cell value by Sheet, row index, and column index | |||||
* | |||||
* @param sheet the Sheet (cannot be null) | |||||
* @param rowIndex 0-based row index | |||||
* @param colIndex 0-based column index | |||||
* @param value the value to set | |||||
*/ | |||||
public static void setCellValue(Sheet sheet, int rowIndex, int colIndex, Object value) { | |||||
setCellValue(getCell(sheet, rowIndex, colIndex), value); | |||||
} | |||||
/** | |||||
* Increase Row Height (if necessary, but never decrease it) by counting the no. | |||||
* of lines in a String value | |||||
* | |||||
* @param sheet The Excel worksheet | |||||
* @param row The row index (0-based) | |||||
* @param value The (multi-line) String value to count for the no. of | |||||
* lines | |||||
* @param heightInPoints The height (in points) for 1 line of text | |||||
*/ | |||||
public static void increaseRowHeight(Sheet sheet, int row, String value, int heightInPoints) { | |||||
int lines = StringUtils.countMatches(value, "\n") + 1; // count no. of lines | |||||
float newHeight = heightInPoints * lines; | |||||
Row r = sheet.getRow(row); | |||||
if (r == null) | |||||
r = sheet.createRow(row); | |||||
// increase the row height if necessary, but never decrease it | |||||
if (r.getHeightInPoints() < newHeight) { | |||||
r.setHeightInPoints(newHeight); | |||||
} | |||||
} | |||||
/** | |||||
* Add merged region (i.e. merge cells) | |||||
* | |||||
* @param sheet The Excel worksheet | |||||
* @param firstRowIdx The first row index (0-based) | |||||
* @param lastRowIdx The last row index (0-based) | |||||
* @param firstColIdx The first column index (0-based) | |||||
* @param lastColIdx The last column index (0-based) | |||||
*/ | |||||
public static void addMergedRegion(Sheet sheet, int firstRowIdx, int lastRowIdx, int firstColIdx, int lastColIdx) { | |||||
CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRowIdx, lastRowIdx, firstColIdx, lastColIdx); | |||||
sheet.addMergedRegion(cellRangeAddress); | |||||
} | |||||
/** | |||||
* Copy and Insert Row | |||||
* | |||||
* @param workbook The Excel workbook | |||||
* @param sourceSheet The source Excel worksheet | |||||
* @param destinationSheet The destination Excel worksheet | |||||
* @param sourceRowNum The source row index (0-based) to copy from | |||||
* @param destinationRowNum The destination row index (0-based) to insert into | |||||
* (from the copied row) | |||||
*/ | |||||
public static void copyAndInsertRow(Workbook workbook, Sheet sourceSheet, Sheet destinationSheet, int sourceRowNum, | |||||
int destinationRowNum) { | |||||
// get the source / destination row | |||||
Row sourceRow = sourceSheet.getRow(sourceRowNum); | |||||
Row destRow = destinationSheet.getRow(destinationRowNum); | |||||
// if the row exist in destination, push down all rows by 1 | |||||
if (destRow != null) { | |||||
destinationSheet.shiftRows(destinationRowNum, destinationSheet.getLastRowNum(), 1, true, false); | |||||
} | |||||
// create a new row | |||||
destRow = destinationSheet.createRow(destinationRowNum); | |||||
// loop through source columns to add to new row | |||||
for (int i = 0; i < sourceRow.getLastCellNum(); i++) { | |||||
// grab a copy of the old cell | |||||
Cell oldCell = sourceRow.getCell(i); | |||||
// if the old cell is null jump to next cell | |||||
if (oldCell == null) | |||||
continue; | |||||
// create a new cell in destination row | |||||
Cell newCell = destRow.createCell(i); | |||||
// apply cell style to new cell from old cell | |||||
newCell.setCellStyle(oldCell.getCellStyle()); | |||||
// if there is a cell comment, copy | |||||
if (oldCell.getCellComment() != null) { | |||||
newCell.setCellComment(oldCell.getCellComment()); | |||||
} | |||||
// if there is a cell hyperlink, copy | |||||
if (oldCell.getHyperlink() != null) { | |||||
newCell.setHyperlink(oldCell.getHyperlink()); | |||||
} | |||||
// copy the cell data type | |||||
newCell.setCellType(oldCell.getCellTypeEnum()); | |||||
// copy the cell data value | |||||
switch (oldCell.getCellTypeEnum()) { | |||||
case NUMERIC: | |||||
newCell.setCellValue(oldCell.getNumericCellValue()); | |||||
break; | |||||
case STRING: | |||||
newCell.setCellValue(oldCell.getRichStringCellValue()); | |||||
break; | |||||
case FORMULA: | |||||
newCell.setCellFormula(oldCell.getCellFormula()); | |||||
break; | |||||
case BLANK: | |||||
newCell.setCellValue(oldCell.getStringCellValue()); | |||||
break; | |||||
case BOOLEAN: | |||||
newCell.setCellValue(oldCell.getBooleanCellValue()); | |||||
break; | |||||
case ERROR: | |||||
newCell.setCellErrorValue(oldCell.getErrorCellValue()); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
// if there are any merged regions in the source row, copy to new row | |||||
for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) { | |||||
CellRangeAddress cellRangeAddress = sourceSheet.getMergedRegion(i); | |||||
if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) { | |||||
addMergedRegion(destinationSheet, destRow.getRowNum(), | |||||
(destRow.getRowNum() + (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow())), | |||||
cellRangeAddress.getFirstColumn(), cellRangeAddress.getLastColumn()); | |||||
} | |||||
} | |||||
// copy row height | |||||
destRow.setHeight(sourceRow.getHeight()); | |||||
} | |||||
/** | |||||
* Copy and Insert Row | |||||
* | |||||
* @param workbook The Excel workbook | |||||
* @param sheet The Excel worksheet | |||||
* @param sourceRowNum The source row index (0-based) to copy from | |||||
* @param destinationRowNum The destination row index (0-based) to insert into | |||||
* (from the copied row) | |||||
*/ | |||||
public static void copyAndInsertRow(Workbook workbook, Sheet sheet, int sourceRowNum, int destinationRowNum) { | |||||
copyAndInsertRow(workbook, sheet, sheet, sourceRowNum, destinationRowNum); | |||||
} | |||||
/** | |||||
* Copy Column | |||||
* | |||||
* @param workbook The Excel workbook | |||||
* @param sourceSheet The source Excel worksheet | |||||
* @param destinationSheet The destination Excel worksheet | |||||
* @param rowStart The source row start index (0-based) to copy from | |||||
* @param rowEnd The source row end index (0-based) to copy from | |||||
* @param sourceColumnNum The source column index (0-based) to copy from | |||||
* @param destinationColumnNum The destination column index (0-based) to copy | |||||
* into (from the copied row) | |||||
*/ | |||||
public static void copyColumn(Workbook workbook, Sheet sourceSheet, Sheet destinationSheet, int rowStart, | |||||
int rowEnd, int sourceColumnNum, int destinationColumnNum) { | |||||
for (int i = rowStart; i <= rowEnd; i++) { | |||||
Row sourceRow = sourceSheet.getRow(i); | |||||
if (sourceRow == null) | |||||
continue; | |||||
Row destinationRow = destinationSheet.getRow(i); | |||||
if (destinationRow == null) | |||||
destinationRow = destinationSheet.createRow(i); | |||||
Cell oldCell = sourceRow.getCell(sourceColumnNum); | |||||
if (oldCell == null) | |||||
continue; | |||||
Cell newCell = destinationRow.createCell(destinationColumnNum); | |||||
newCell.setCellStyle(oldCell.getCellStyle()); | |||||
if (oldCell.getCellComment() != null) { | |||||
newCell.setCellComment(oldCell.getCellComment()); | |||||
} | |||||
if (oldCell.getHyperlink() != null) { | |||||
newCell.setHyperlink(oldCell.getHyperlink()); | |||||
} | |||||
newCell.setCellType(oldCell.getCellTypeEnum()); | |||||
switch (oldCell.getCellTypeEnum()) { | |||||
case NUMERIC: | |||||
newCell.setCellValue(oldCell.getNumericCellValue()); | |||||
break; | |||||
case STRING: | |||||
newCell.setCellValue(oldCell.getRichStringCellValue()); | |||||
break; | |||||
case FORMULA: | |||||
newCell.setCellFormula(oldCell.getCellFormula()); | |||||
break; | |||||
case BLANK: | |||||
newCell.setCellValue(oldCell.getStringCellValue()); | |||||
break; | |||||
case BOOLEAN: | |||||
newCell.setCellValue(oldCell.getBooleanCellValue()); | |||||
break; | |||||
case ERROR: | |||||
newCell.setCellErrorValue(oldCell.getErrorCellValue()); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
for (int ii = 0; ii < sourceSheet.getNumMergedRegions(); ii++) { | |||||
CellRangeAddress cellRangeAddress = sourceSheet.getMergedRegion(ii); | |||||
if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) { | |||||
addMergedRegion(destinationSheet, cellRangeAddress.getFirstRow(), cellRangeAddress.getLastRow(), | |||||
destinationColumnNum, (destinationColumnNum | |||||
+ (cellRangeAddress.getLastColumn() - cellRangeAddress.getFirstColumn()))); | |||||
} | |||||
} | |||||
} | |||||
destinationSheet.setColumnWidth(destinationColumnNum, sourceSheet.getColumnWidth(sourceColumnNum)); | |||||
} | |||||
/** | |||||
* Copy Column | |||||
* | |||||
* @param workbook The Excel workbook | |||||
* @param sheet The Excel worksheet | |||||
* @param rowStart The source row start index (0-based) to copy from | |||||
* @param rowEnd The source row end index (0-based) to copy from | |||||
* @param sourceColumnNum The source column index (0-based) to copy from | |||||
* @param destinationColumnNum The destination column index (0-based) to copy | |||||
* into (from the copied row) | |||||
*/ | |||||
public static void copyColumn(Workbook workbook, Sheet sheet, int rowStart, int rowEnd, int sourceColumnNum, | |||||
int destinationColumnNum) { | |||||
copyColumn(workbook, sheet, sheet, rowStart, rowEnd, sourceColumnNum, destinationColumnNum); | |||||
} | |||||
public static void shiftColumns(Row row, int startingIndex, int shiftCount) { | |||||
for (int i = row.getPhysicalNumberOfCells() - 1; i >= startingIndex; i--) { | |||||
Cell oldCell = row.getCell(i); | |||||
Cell newCell = row.createCell(i + shiftCount); | |||||
// apply cell style to new cell from old cell | |||||
newCell.setCellStyle(oldCell.getCellStyle()); | |||||
// if there is a cell comment, copy | |||||
if (oldCell.getCellComment() != null) { | |||||
newCell.setCellComment(oldCell.getCellComment()); | |||||
} | |||||
// if there is a cell hyperlink, copy | |||||
if (oldCell.getHyperlink() != null) { | |||||
newCell.setHyperlink(oldCell.getHyperlink()); | |||||
} | |||||
// copy the cell data type | |||||
newCell.setCellType(oldCell.getCellTypeEnum()); | |||||
// copy the cell data value | |||||
switch (oldCell.getCellTypeEnum()) { | |||||
case NUMERIC: | |||||
newCell.setCellValue(oldCell.getNumericCellValue()); | |||||
break; | |||||
case STRING: | |||||
newCell.setCellValue(oldCell.getRichStringCellValue()); | |||||
break; | |||||
case FORMULA: | |||||
newCell.setCellFormula(oldCell.getCellFormula()); | |||||
break; | |||||
case BLANK: | |||||
newCell.setCellValue(oldCell.getStringCellValue()); | |||||
break; | |||||
case BOOLEAN: | |||||
newCell.setCellValue(oldCell.getBooleanCellValue()); | |||||
break; | |||||
case ERROR: | |||||
newCell.setCellErrorValue(oldCell.getErrorCellValue()); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/** handle some invalid char included ( /\*[]:? ) */ | |||||
public static void setSheetName(Workbook workbook, Sheet sheet, String name) { | |||||
if (workbook != null && sheet != null && StringUtils.isNotBlank(name)) | |||||
workbook.setSheetName(workbook.getSheetIndex(sheet), name.replaceAll("[/\\\\*\\[\\]:\\?]", "_")); | |||||
} | |||||
/** delete row */ | |||||
public static void deleteRow(Sheet sheet, int rowIndex) { | |||||
if (sheet != null) { | |||||
sheet.removeRow(sheet.getRow(rowIndex)); | |||||
if (rowIndex < sheet.getLastRowNum()) | |||||
sheet.shiftRows(rowIndex, sheet.getLastRowNum(), -1); | |||||
} | |||||
} | |||||
public static byte[] encrypt(Workbook workbook, String password) { | |||||
return encrypt(toByteArray(workbook), password); | |||||
} | |||||
public static byte[] encrypt(byte[] bytes, String password) { | |||||
POIFSFileSystem fs = new POIFSFileSystem(); | |||||
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); | |||||
// EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, | |||||
// CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null); | |||||
Encryptor enc = info.getEncryptor(); | |||||
enc.confirmPassword(password); | |||||
// Read in an existing OOXML file and write to encrypted output stream | |||||
// don't forget to close the output stream otherwise the padding bytes aren't | |||||
// added | |||||
try { | |||||
OPCPackage opc = OPCPackage.open(new ByteArrayInputStream(bytes)); | |||||
OutputStream os = enc.getDataStream(fs); | |||||
opc.save(os); | |||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||||
fs.writeFilesystem(bos); | |||||
return bos.toByteArray(); | |||||
} catch (InvalidFormatException e) { | |||||
e.printStackTrace(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} catch (GeneralSecurityException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
return bytes; | |||||
} | |||||
} |
@@ -0,0 +1,122 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2013 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
/** | |||||
* File Utils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class FileUtils { | |||||
private static final Map<String, String> MIMETYPES = new HashMap<>(); | |||||
static { | |||||
MIMETYPES.put("pdf", "application/pdf"); | |||||
MIMETYPES.put("doc", "application/msword"); | |||||
MIMETYPES.put("dot", "application/msword"); | |||||
MIMETYPES.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); | |||||
MIMETYPES.put("xls", "application/vnd.ms-excel"); | |||||
MIMETYPES.put("xlm", "application/vnd.ms-excel"); | |||||
MIMETYPES.put("xla", "application/vnd.ms-excel"); | |||||
MIMETYPES.put("xlc", "application/vnd.ms-excel"); | |||||
MIMETYPES.put("xlt", "application/vnd.ms-excel"); | |||||
MIMETYPES.put("xlw", "application/vnd.ms-excel"); | |||||
MIMETYPES.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); | |||||
MIMETYPES.put("ppt", "application/vnd.ms-powerpoint"); | |||||
MIMETYPES.put("pps", "application/vnd.ms-powerpoint"); | |||||
MIMETYPES.put("pot", "application/vnd.ms-powerpoint"); | |||||
MIMETYPES.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); | |||||
MIMETYPES.put("bat", "application/x-msdownload"); | |||||
MIMETYPES.put("com", "application/x-msdownload"); | |||||
MIMETYPES.put("dll", "application/x-msdownload"); | |||||
MIMETYPES.put("exe", "application/x-msdownload"); | |||||
MIMETYPES.put("msi", "application/x-msdownload"); | |||||
MIMETYPES.put("swf", "application/x-shockwave-flash"); | |||||
MIMETYPES.put("7z", "application/x-7z-compressed"); | |||||
MIMETYPES.put("rar", "application/x-rar-compressed"); | |||||
MIMETYPES.put("zip", "application/zip"); | |||||
MIMETYPES.put("js", "application/javascript"); | |||||
MIMETYPES.put("json", "application/json"); | |||||
MIMETYPES.put("mpga", "audio/mpeg"); | |||||
MIMETYPES.put("mp2", "audio/mpeg"); | |||||
MIMETYPES.put("mp2a", "audio/mpeg"); | |||||
MIMETYPES.put("mp3", "audio/mpeg"); | |||||
MIMETYPES.put("m2a", "audio/mpeg"); | |||||
MIMETYPES.put("m3a", "audio/mpeg"); | |||||
MIMETYPES.put("bmp", "image/bmp"); | |||||
MIMETYPES.put("gif", "image/gif"); | |||||
MIMETYPES.put("jpeg", "image/jpeg"); | |||||
MIMETYPES.put("jpg", "image/jpeg"); | |||||
MIMETYPES.put("jpe", "image/jpeg"); | |||||
MIMETYPES.put("JPG", "image/jpeg"); | |||||
MIMETYPES.put("png", "image/png"); | |||||
MIMETYPES.put("PNG", "image/png"); | |||||
MIMETYPES.put("tiff", "image/tiff"); | |||||
MIMETYPES.put("tif", "image/tiff"); | |||||
MIMETYPES.put("css", "text/css"); | |||||
MIMETYPES.put("csv", "text/csv"); | |||||
MIMETYPES.put("html", "text/html"); | |||||
MIMETYPES.put("htm", "text/html"); | |||||
MIMETYPES.put("txt", "text/plain"); | |||||
MIMETYPES.put("text", "text/plain"); | |||||
MIMETYPES.put("conf", "text/plain"); | |||||
MIMETYPES.put("log", "text/plain"); | |||||
MIMETYPES.put("mp4", "video/mp4"); | |||||
MIMETYPES.put("mp4v", "video/mp4"); | |||||
MIMETYPES.put("mpg4", "video/mp4"); | |||||
MIMETYPES.put("mpeg", "video/mpeg"); | |||||
MIMETYPES.put("mpg", "video/mpeg"); | |||||
MIMETYPES.put("mpe", "video/mpeg"); | |||||
MIMETYPES.put("m1v", "video/mpeg"); | |||||
MIMETYPES.put("m2v", "video/mpeg"); | |||||
MIMETYPES.put("qt", "video/quicktime"); | |||||
MIMETYPES.put("mov", "video/quicktime"); | |||||
MIMETYPES.put("wmv", "video/x-ms-wmv"); | |||||
MIMETYPES.put("wmx", "video/x-ms-wmx"); | |||||
MIMETYPES.put("wvx", "video/x-ms-wvx"); | |||||
MIMETYPES.put("avi", "video/x-msvideo"); | |||||
// MIMETYPES.put("xxxxx", "xxxxx"); | |||||
} | |||||
/** | |||||
* Guess the mimetype from the file name extension | |||||
* | |||||
* @return The mimetype guessed from the file name extension, or {@code null} if the mimetype cannot be determined | |||||
*/ | |||||
public static String guessMimetype(String filename) { | |||||
String extension = StringUtils.substringAfterLast(filename, "."); | |||||
String mimetype = MIMETYPES.get(extension); | |||||
return mimetype != null ? mimetype : "application/octet-stream"; | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.awt.image.BufferedImage; | |||||
import org.imgscalr.Scalr; | |||||
/** | |||||
* Image Utils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class ImageUtils { | |||||
public BufferedImage resize(BufferedImage srcImage, int targetSize) { | |||||
return Scalr.resize(srcImage, targetSize); | |||||
} | |||||
} |
@@ -0,0 +1,68 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.io.IOException; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import com.fasterxml.jackson.core.JsonParseException; | |||||
import com.fasterxml.jackson.core.JsonProcessingException; | |||||
import com.fasterxml.jackson.core.type.TypeReference; | |||||
import com.fasterxml.jackson.databind.JsonMappingException; | |||||
import com.fasterxml.jackson.databind.ObjectMapper; | |||||
/** | |||||
* JSON Utils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class JsonUtils { | |||||
// Default mapper instance | |||||
private static final ObjectMapper mapper = new ObjectMapper(); | |||||
/** | |||||
* Method that can be used to serialize any Java value as a JSON String. | |||||
*/ | |||||
public static String toJsonString(Object obj) { | |||||
try { | |||||
return mapper.writeValueAsString(obj); | |||||
} catch (JsonProcessingException e) { | |||||
return null; | |||||
} | |||||
} | |||||
/** | |||||
* Read from JSON String. | |||||
* | |||||
* @param content JSON String content | |||||
* @param valueType the return type | |||||
*/ | |||||
public static <T> T fromJsonString(String content, Class<T> valueType) | |||||
throws JsonParseException, JsonMappingException, IOException { | |||||
return mapper.readValue(content, valueType); | |||||
} | |||||
public static Map<String, Object> fromJsonStringAsMap(String content) | |||||
throws JsonParseException, JsonMappingException, IOException { | |||||
return mapper.readValue(content, new TypeReference<Map<String, Object>>() { | |||||
}); | |||||
} | |||||
public static List<Map<String, Object>> fromJsonStringAsListOfMap(String content) | |||||
throws JsonParseException, JsonMappingException, IOException { | |||||
return mapper.readValue(content, new TypeReference<List<Map<String, Object>>>() { | |||||
}); | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
package com.ffii.core.utils; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | |||||
* ListUtils | |||||
* | |||||
*/ | |||||
public class ListUtils { | |||||
/** | |||||
* Convert values to ArrayList | |||||
* | |||||
* @param values | |||||
* | |||||
* @return List | |||||
*/ | |||||
@SuppressWarnings("unchecked") | |||||
public static <V> List<V> toArrayList(V... values) { | |||||
List<V> list = new ArrayList<V>(values.length); | |||||
for (int i = 0; i < values.length; i++) | |||||
list.add(values[i]); | |||||
return list; | |||||
} | |||||
} |
@@ -0,0 +1,47 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.util.Locale; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import org.springframework.util.StringUtils; | |||||
import org.springframework.web.servlet.LocaleResolver; | |||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver; | |||||
/** | |||||
* Utility class for performing translation between Locale and String representations. | |||||
*/ | |||||
public abstract class LocaleUtils { | |||||
private static final LocaleResolver localeResolver = new SessionLocaleResolver(); | |||||
public static final String toString(Locale locale) { | |||||
return locale.toString(); | |||||
} | |||||
public static final Locale toLocale(String localeString) { | |||||
return StringUtils.parseLocaleString(localeString); | |||||
} | |||||
public static final Locale resolveLocale(HttpServletRequest request) { | |||||
return localeResolver.resolveLocale(request); | |||||
} | |||||
public static final String resolveLocaleString(HttpServletRequest request) { | |||||
return resolveLocale(request).toString(); | |||||
} | |||||
public static final void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { | |||||
localeResolver.setLocale(request, response, locale); | |||||
} | |||||
} |
@@ -0,0 +1,34 @@ | |||||
package com.ffii.core.utils; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
/** | |||||
* @author fung | |||||
*/ | |||||
public class MapBuilder<K, V> { | |||||
private Map<K, V> args; | |||||
public MapBuilder() { | |||||
args = new HashMap<K, V>(); | |||||
} | |||||
public MapBuilder(Map<K, V> args) { | |||||
this.args = args; | |||||
} | |||||
public MapBuilder<K, V> add(K key, V value) { | |||||
args.put(key, value); | |||||
return this; | |||||
} | |||||
public Map<K, V> toMap() { | |||||
return args; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return args.toString(); | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
/** | |||||
* MapUtils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class MapUtils { | |||||
/** | |||||
* Convert key-value pairs to HashMap | |||||
* | |||||
* @param keyValuePairs | |||||
* Keys and values must be in pairs | |||||
* | |||||
* @return Map | |||||
*/ | |||||
@SuppressWarnings("unchecked") | |||||
public static <K, V> Map<K, V> toHashMap(Object... keyValuePairs) { | |||||
if (keyValuePairs.length % 2 != 0) | |||||
throw new IllegalArgumentException("Keys and values must be in pairs"); | |||||
Map<K, V> map = new HashMap<K, V>(keyValuePairs.length / 2); | |||||
for (int i = 0; i < keyValuePairs.length; i += 2) { | |||||
map.put((K) keyValuePairs[i], (V) keyValuePairs[i + 1]); | |||||
} | |||||
return map; | |||||
} | |||||
} |
@@ -0,0 +1,471 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.math.BigDecimal; | |||||
import java.math.RoundingMode; | |||||
import org.apache.commons.lang3.ArrayUtils; | |||||
/** | |||||
* NumberUtils extends from Apache Commons, and incl some methods from MathUtils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class NumberUtils extends org.apache.commons.lang3.math.NumberUtils { | |||||
/** | |||||
* Round the given value to the specified number of decimal places. The value is rounded using the given method which is any method defined in | |||||
* {@link BigDecimal}. | |||||
* | |||||
* @param x | |||||
* the value to round | |||||
* @param scale | |||||
* the number of digits to the right of the decimal point | |||||
* @param roundingMethod | |||||
* the rounding method as defined in {@link RoundingMode} | |||||
* @return the rounded value | |||||
*/ | |||||
public static double round(double x, int scale, RoundingMode roundingMethod) { | |||||
try { | |||||
return BigDecimal.valueOf(x).setScale(scale, roundingMethod).doubleValue(); | |||||
} catch (NumberFormatException ex) { | |||||
if (Double.isInfinite(x)) { | |||||
return x; | |||||
} else { | |||||
return Double.NaN; | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Round the given value to the specified number of decimal places. The value is rounded using the {@link RoundingMode#HALF_UP} method. | |||||
* | |||||
* @param x | |||||
* the value to round | |||||
* @param scale | |||||
* the number of digits to the right of the decimal point | |||||
* @return the rounded value | |||||
* @see org.apache.commons.math.util.MathUtils#round(double, int) | |||||
*/ | |||||
public static double round(double x, int scale) { | |||||
return round(x, scale, RoundingMode.HALF_UP); | |||||
} | |||||
/** | |||||
* Round up the given value to the specified number of decimal places. The value is rounded up using the {@link RoundingMode#UP} method. | |||||
* | |||||
* @param x | |||||
* the value to round up | |||||
* @param scale | |||||
* the number of digits to the right of the decimal point | |||||
* @return the rounded up value | |||||
*/ | |||||
public static double roundUp(double x, int scale) { | |||||
return round(x, scale, RoundingMode.UP); | |||||
} | |||||
/** | |||||
* Round down the given value to the specified number of decimal places. The value is rounded down using the {@link RoundingMode#DOWN} method. | |||||
* | |||||
* @param x | |||||
* the value to round down | |||||
* @param scale | |||||
* the number of digits to the right of the decimal point | |||||
* @return the rounded down value | |||||
*/ | |||||
public static double roundDown(double x, int scale) { | |||||
return round(x, scale, RoundingMode.DOWN); | |||||
} | |||||
/** | |||||
* Return the {@code int} value of an {@code Object}, or the default value if the object is either {@code null} or not an instance of {@code Number}. | |||||
* | |||||
* @param obj | |||||
* the {@code Object} | |||||
* @param defaultValue | |||||
* the default value | |||||
* @return the {@code int} value of the {@code Object}, or the default if the object is either {@code null} or not an instance of {@code Number} | |||||
* @see Number#intValue() | |||||
*/ | |||||
public static int intValue(Object obj, int defaultValue) { | |||||
if (obj instanceof Number) { | |||||
return ((Number) obj).intValue(); | |||||
} else if (obj instanceof String) { | |||||
try { | |||||
return Integer.parseInt((String) obj); | |||||
} catch (NumberFormatException nfe) { | |||||
} | |||||
} | |||||
return defaultValue; | |||||
} | |||||
/** | |||||
* Return the {@code int} value of an {@code Object}, or {@code zero} if the object is either {@code null} or not an instance of {@code Number}. | |||||
* | |||||
* @param obj | |||||
* the {@code Object} | |||||
* @return the {@code int} value of the {@code Object}, or {@code zero} if the object is either {@code null} or not an instance of {@code Number} | |||||
* @see Number#intValue() | |||||
*/ | |||||
public static int intValue(Object obj) { | |||||
return intValue(obj, 0); | |||||
} | |||||
/** | |||||
* Convert an {@code Object} to an {@code Integer} (only if the object is an instance of {@code Number} or {@code String}), returning a default value if the | |||||
* conversion fails. | |||||
* <p> | |||||
* If the object is {@code null}, the default value is returned. | |||||
* | |||||
* @param obj | |||||
* the object to convert, may be {@code null} | |||||
* @param defaultValue | |||||
* the default value, may be {@code null} | |||||
* @return the Integer represented by the object, or the default if conversion fails | |||||
*/ | |||||
public static Integer toInt(Object obj, Integer defaultValue) { | |||||
if (obj instanceof Number) | |||||
return Integer.valueOf(((Number) obj).intValue()); | |||||
else if (obj instanceof String) | |||||
try { | |||||
return Integer.valueOf((String) obj); | |||||
} catch (NumberFormatException nfe) { | |||||
return defaultValue; | |||||
} | |||||
else | |||||
return defaultValue; | |||||
} | |||||
/** | |||||
* Return the {@code long} value of a {@code Long} object, or a default value if the object is {@code null}. | |||||
* | |||||
* @param obj | |||||
* the object (can be {@code null}) | |||||
* @param defaultValue | |||||
* the default value | |||||
* @return the {@code long} value of the object if it's a number, or the default value if the object is {@code null} or {@code NaN} | |||||
* @see Long#longValue() | |||||
*/ | |||||
public static long longValue(Object obj, long defaultValue) { | |||||
return (obj instanceof Number) ? ((Number) obj).longValue() : defaultValue; | |||||
} | |||||
/** | |||||
* Return the {@code long} value of a {@code Long} object, or {@code zero} if the object is {@code null}. | |||||
* | |||||
* @param obj | |||||
* the object (can be {@code null}) | |||||
* @return the {@code long} value of the object if it's a number, or {@code zero} if the object is {@code null} or {@code NaN} | |||||
* @see Long#longValue() | |||||
*/ | |||||
public static long longValue(Object obj) { | |||||
return longValue(obj, 0l); | |||||
} | |||||
/** | |||||
* Convert an {@code Object} to a {@code Long} (only if the object is an instance of {@code Number} or {@code String}), returning a default value if the | |||||
* conversion fails. | |||||
* <p> | |||||
* If the object is {@code null}, the default value is returned. | |||||
* | |||||
* @param obj | |||||
* the object to convert, may be {@code null} | |||||
* @param defaultValue | |||||
* the default value, may be {@code null} | |||||
* @return the Long represented by the object, or the default if conversion fails | |||||
*/ | |||||
public static Long toLong(Object obj, Long defaultValue) { | |||||
if (obj instanceof Number) | |||||
return Long.valueOf(((Number) obj).longValue()); | |||||
else if (obj instanceof String) | |||||
try { | |||||
return Long.valueOf((String) obj); | |||||
} catch (NumberFormatException nfe) { | |||||
return defaultValue; | |||||
} | |||||
else | |||||
return defaultValue; | |||||
} | |||||
/** | |||||
* @param obj | |||||
* the object (can be {@code null}) | |||||
* @param defaultValue | |||||
* the default value | |||||
* @return the {@code double} value of the object if it's a number, or the default value if the object is {@code null} or {@code NaN} | |||||
* @see Double#doubleValue() | |||||
*/ | |||||
public static double doubleValue(Object obj, double defaultValue) { | |||||
return (obj instanceof Number) ? ((Number) obj).doubleValue() : defaultValue; | |||||
} | |||||
/** | |||||
* @param obj | |||||
* the object (can be {@code null}) | |||||
* @param defaultValue | |||||
* the default value | |||||
* @return the {@code double} value of the object if it's a number, or {@code zero} if the object is {@code null} or {@code NaN} | |||||
* @see Double#doubleValue() | |||||
*/ | |||||
public static double doubleValue(Object obj) { | |||||
return doubleValue(obj, 0.0d); | |||||
} | |||||
/** | |||||
* Convert an {@code Object} to a {@code Double} (only if the object is an instance of {@code Number} or {@code String}), returning a default value if the | |||||
* conversion fails. | |||||
* <p> | |||||
* If the object is {@code null}, the default value is returned. | |||||
* | |||||
* @param obj | |||||
* the object to convert, may be {@code null} | |||||
* @param defaultValue | |||||
* the default value, may be {@code null} | |||||
* @return the Double represented by the object, or the default if conversion fails | |||||
*/ | |||||
public static Double toDouble(Object obj, Double defaultValue) { | |||||
if (obj instanceof Number) | |||||
return Double.valueOf(((Number) obj).doubleValue()); | |||||
else if (obj instanceof String) | |||||
try { | |||||
return Double.valueOf((String) obj); | |||||
} catch (NumberFormatException nfe) { | |||||
return defaultValue; | |||||
} | |||||
else | |||||
return defaultValue; | |||||
} | |||||
/** | |||||
* Return the {@code BigDecimal} object, or {@code zero} if the object is {@code null}. | |||||
* | |||||
* @param obj | |||||
* the {@code BigDecimal} object | |||||
* @return the {@code BigDecimal} object, or {@code zero} if the object is {@code null} | |||||
*/ | |||||
public static BigDecimal decimalValue(BigDecimal obj) { | |||||
return decimalValue(obj, BigDecimal.ZERO); | |||||
} | |||||
/** | |||||
* Return the {@code BigDecimal} object, or a default value if the object is {@code null}. | |||||
* | |||||
* @param obj | |||||
* the {@code BigDecimal} object | |||||
* @param defaultValue | |||||
* the default value | |||||
* @return the {@code BigDecimal} object, or the default if the object is {@code null} | |||||
*/ | |||||
public static BigDecimal decimalValue(BigDecimal obj, BigDecimal defaultValue) { | |||||
return obj == null ? defaultValue : obj; | |||||
} | |||||
/** | |||||
* Convert an {@code Object} to a {@code BigDecimal}, returning {@code BigDecimal.ZERO} if the conversion fails (e.g. the object is not an instance of | |||||
* {@code Number} nor {@code String}). | |||||
* <p> | |||||
* If the object is {@code null}, {@code BigDecimal.ZERO} is returned. | |||||
* | |||||
* @param obj | |||||
* the object to convert, may be {@code null} | |||||
* @return the BigDecimal represented by the object, or {@code BigDecimal.ZERO} if conversion fails | |||||
*/ | |||||
public static BigDecimal toDecimal(Object obj) { | |||||
return toDecimal(obj, BigDecimal.ZERO); | |||||
} | |||||
/** | |||||
* Convert an {@code Object} to a {@code BigDecimal}, returning a default value if the conversion fails (e.g. the object is not an instance of | |||||
* {@code Number} nor {@code String}). | |||||
* <p> | |||||
* If the object is {@code null}, the default value is returned. | |||||
* | |||||
* @param obj | |||||
* the object to convert, may be {@code null} | |||||
* @param defaultValue | |||||
* the default value, may be {@code null} | |||||
* @return the BigDecimal represented by the object, or the default if conversion fails | |||||
*/ | |||||
public static BigDecimal toDecimal(Object obj, BigDecimal defaultValue) { | |||||
if (obj instanceof BigDecimal) | |||||
return (BigDecimal) obj; | |||||
else if (obj instanceof Number) | |||||
return BigDecimal.valueOf(((Number) obj).doubleValue()); | |||||
else if (obj instanceof String) | |||||
try { | |||||
return new BigDecimal((String) obj); | |||||
} catch (NumberFormatException nfe) { | |||||
return defaultValue; | |||||
} | |||||
else | |||||
return defaultValue; | |||||
} | |||||
/** | |||||
* Null-safe method to check if the two {@code Integer} objects have the same value. | |||||
* | |||||
* <ol> | |||||
* <li>Returns {@code true} if {@code a} and {@code b} are both {@code null}. | |||||
* <li>Returns {@code false} if only one of them is {@code null}. | |||||
* <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have the same {@code int} value, else returns {@code false}. | |||||
* </ol> | |||||
* | |||||
* @param a | |||||
* Integer obj, may be {@code null} | |||||
* @param b | |||||
* Integer obj, may be {@code null} | |||||
*/ | |||||
public static boolean isEqual(Integer a, Integer b) { | |||||
return a == null ? (b == null ? true : false) : (b == null ? false : a.equals(b)); | |||||
} | |||||
/** | |||||
* Null-safe method to check if the two {@code Integer} objects have different values. | |||||
* | |||||
* <ol> | |||||
* <li>Returns {@code false} if {@code a} and {@code b} are both {@code null}. | |||||
* <li>Returns {@code true} if only one of them is {@code null}. | |||||
* <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have different {@code int} values, else returns {@code false}. | |||||
* </ol> | |||||
* | |||||
* @param a | |||||
* Integer obj, may be {@code null} | |||||
* @param b | |||||
* Integer obj, may be {@code null} | |||||
*/ | |||||
public static boolean isNotEqual(Integer a, Integer b) { | |||||
return !isEqual(a, b); | |||||
} | |||||
/** | |||||
* Null-safe method to check if the two {@code BigDecimal} objects have the same value. | |||||
* <p> | |||||
* Two {@code BigDecimal} objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. | |||||
* | |||||
* <ol> | |||||
* <li>Returns {@code true} if {@code a} and {@code b} are both {@code null}. | |||||
* <li>Returns {@code false} if only one of them is {@code null}. | |||||
* <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have the same {@code decimal} value, else returns {@code false}. | |||||
* </ol> | |||||
* | |||||
* @param a | |||||
* BigDecimal obj, may be {@code null} | |||||
* @param b | |||||
* BigDecimal obj, may be {@code null} | |||||
*/ | |||||
public static boolean isEqual(BigDecimal a, BigDecimal b) { | |||||
return a == null ? (b == null ? true : false) : (b == null ? false : a.compareTo(b) == 0); | |||||
} | |||||
/** | |||||
* Null-safe method to check if the two {@code BigDecimal} objects have different values. | |||||
* <p> | |||||
* Two {@code BigDecimal} objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. | |||||
* | |||||
* <ol> | |||||
* <li>Returns {@code false} if {@code a} and {@code b} are both {@code null}. | |||||
* <li>Returns {@code true} if only one of them is {@code null}. | |||||
* <li>Returns {@code true} if {@code a} and {@code b} are not {@code null} and have different {@code decimal} values, else returns {@code false}. | |||||
* </ol> | |||||
* | |||||
* @param a | |||||
* BigDecimal obj, may be {@code null} | |||||
* @param b | |||||
* BigDecimal obj, may be {@code null} | |||||
*/ | |||||
public static boolean isNotEqual(BigDecimal a, BigDecimal b) { | |||||
return !isEqual(a, b); | |||||
} | |||||
/** | |||||
* Check if {@code BigDecimal} object {@code a} is greater than {@code BigDecimal} object {@code b}. | |||||
* | |||||
* @param a | |||||
* non-{@code null} BigDecimal obj | |||||
* @param b | |||||
* non-{@code null} BigDecimal obj | |||||
*/ | |||||
public static boolean isGreaterThan(BigDecimal a, BigDecimal b) { | |||||
return a.compareTo(b) > 0; | |||||
} | |||||
/** | |||||
* Check if {@code BigDecimal} object {@code a} is greater than or equals to {@code BigDecimal} object {@code b}. | |||||
* | |||||
* @param a | |||||
* non-{@code null} BigDecimal obj | |||||
* @param b | |||||
* non-{@code null} BigDecimal obj | |||||
*/ | |||||
public static boolean isGreaterThanOrEqual(BigDecimal a, BigDecimal b) { | |||||
return a.compareTo(b) >= 0; | |||||
} | |||||
/** | |||||
* Check if {@code BigDecimal} object {@code a} is less than {@code BigDecimal} object {@code b}. | |||||
* | |||||
* @param a | |||||
* non-{@code null} BigDecimal obj | |||||
* @param b | |||||
* non-{@code null} BigDecimal obj | |||||
*/ | |||||
public static boolean isLessThan(BigDecimal a, BigDecimal b) { | |||||
return a.compareTo(b) < 0; | |||||
} | |||||
/** | |||||
* Check if {@code BigDecimal} object {@code a} is less than or equals to {@code BigDecimal} object {@code b}. | |||||
* | |||||
* @param a | |||||
* non-{@code null} BigDecimal obj | |||||
* @param b | |||||
* non-{@code null} BigDecimal obj | |||||
*/ | |||||
public static boolean isLessThanOrEqual(BigDecimal a, BigDecimal b) { | |||||
return a.compareTo(b) <= 0; | |||||
} | |||||
/** | |||||
* | |||||
* <pre> | |||||
* NumberUtils.equalsAny(null, (Integer[]) null) = false | |||||
* NumberUtils.equalsAny(null, null, null) = true | |||||
* NumberUtils.equalsAny(null, 1, 2) = false | |||||
* NumberUtils.equalsAny(1, null, 2) = false | |||||
* NumberUtils.equalsAny(1, 1, 2) = true | |||||
* </pre> | |||||
* | |||||
* @param int | |||||
* to compare, may be {@code null}. | |||||
* @param searchInts | |||||
* a int, may be {@code null}. | |||||
* @return {@code true} if the num is equal to any other element of <code>searchInts</code>; {@code false} if <code>searchInts</code> is null or contains no | |||||
* matches. | |||||
*/ | |||||
public static boolean equalsAny(final int num, int... searchInts) { | |||||
if (ArrayUtils.isNotEmpty(searchInts)) { | |||||
for (int next : searchInts) { | |||||
if (num == next) { | |||||
return true; | |||||
} | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
public static double sum(double... nums) { | |||||
BigDecimal rs = BigDecimal.ZERO; | |||||
for (double num : nums) | |||||
rs = rs.add(BigDecimal.valueOf(num)); | |||||
return rs.doubleValue(); | |||||
} | |||||
} |
@@ -0,0 +1,81 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
/** | |||||
* Static strings for standard params | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class Params { | |||||
public static final String ID = "id"; | |||||
public static final String TYPE = "type"; | |||||
public static final String QUERY = "query"; | |||||
public static final String CODE = "code"; | |||||
public static final String NAME = "name"; | |||||
public static final String TITLE = "title"; | |||||
public static final String KEY = "key"; | |||||
public static final String VALUE = "value"; | |||||
public static final String SUCCESS = "success"; | |||||
public static final String AUTH = "auth"; | |||||
/** Short for "message" */ | |||||
public static final String MSG = "msg"; | |||||
public static final String DETAILS = "details"; | |||||
public static final String DATA = "data"; | |||||
public static final String RECORDS = "records"; | |||||
public static final String ROOT = "root"; | |||||
public static final String NODE = "node"; | |||||
public static final String EXPANDED = "expanded"; | |||||
public static final String FROM = "from"; | |||||
public static final String TO = "to"; | |||||
public static final String START = "start"; | |||||
public static final String LIMIT = "limit"; | |||||
public static final String PREFIX = "prefix"; | |||||
public static final String SUFFIX = "suffix"; | |||||
public static final String LENGTH = "length"; | |||||
public static final String MODE = "mode"; | |||||
public static final String COUNT = "count"; | |||||
public static final String TOTAL = "total"; | |||||
public static final String STATUS = "status"; | |||||
public static final String VERSION_ID = "versionId"; | |||||
public static final String REF_ID = "refId"; | |||||
public static final String REF_TYPE = "refType"; | |||||
public static final String REF_CODE = "refCode"; | |||||
public static final String FROM_DATE = "fromDate"; | |||||
public static final String TO_DATE = "toDate"; | |||||
public static final String MAX_ROWS = "maxRows"; | |||||
public static final String METHOD_GET = "GET"; | |||||
public static final String METHOD_POST = "POST"; | |||||
} |
@@ -0,0 +1,225 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.Random; | |||||
import java.util.regex.Pattern; | |||||
import com.ffii.core.User; | |||||
import com.ffii.tbms.user.service.UserService; | |||||
import org.springframework.dao.DataAccessException; | |||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |||||
import org.springframework.security.core.Authentication; | |||||
import org.springframework.security.core.GrantedAuthority; | |||||
import org.springframework.security.core.context.SecurityContext; | |||||
import org.springframework.security.core.context.SecurityContextHolder; | |||||
import org.springframework.security.core.userdetails.UserDetails; | |||||
import org.springframework.security.core.userdetails.UserDetailsService; | |||||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||||
/** | |||||
* Security Utils - for Spring Security | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public class SecurityUtils { | |||||
private static final Pattern PATTERN_DIGITS = Pattern.compile("[0-9]"); | |||||
private static final Pattern PATTERN_A2Z_LOWER = Pattern.compile("[a-z]"); | |||||
private static final Pattern PATTERN_A2Z_UPPER = Pattern.compile("[A-Z]"); | |||||
private static final String A2Z_LOWER = "abcdefghijklmnopqrstuvwxyz"; | |||||
private static final String A2Z_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |||||
private static final String DIGITS = "0123456789"; | |||||
/* | |||||
* Ref: https://www.owasp.org/index.php/Password_special_characters | |||||
* without space character | |||||
*/ | |||||
private static final String SPECIAL_CHARS = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; | |||||
private static Pattern PATTERN_SPECIAL_CHARS = Pattern.compile("[!\"#$%&'()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~]"); | |||||
/** | |||||
* Obtains the current {@code SecurityContext}. | |||||
* | |||||
* @return the security context (never {@code null}) | |||||
*/ | |||||
public static final SecurityContext getSecurityContext() { | |||||
return SecurityContextHolder.getContext(); | |||||
} | |||||
/** | |||||
* @return the authenticated {@code Principal} ({@code User}) | |||||
* @see Authentication#getPrincipal() | |||||
*/ | |||||
public static final User getUser() { | |||||
try { | |||||
return (User) getSecurityContext().getAuthentication().getPrincipal(); | |||||
} catch (ClassCastException e) { | |||||
// no authenticated principal | |||||
return null; | |||||
} catch (NullPointerException e) { | |||||
// no authentication information is available | |||||
return null; | |||||
} | |||||
} | |||||
/** | |||||
* Updates the Authentication Token with the user (e.g. user changed the password) | |||||
* | |||||
* @see SecurityContext#setAuthentication(Authentication) | |||||
*/ | |||||
public static final void updateUserAuthentication(final User user) { | |||||
getSecurityContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities())); | |||||
} | |||||
/** | |||||
* Checks if the current user is GRANTED the {@code role} | |||||
* | |||||
* @param role | |||||
* the {@code role} to check for | |||||
* @return {@code true} if the current user is GRANTED the {@code role}, else {@code false} | |||||
*/ | |||||
public static final boolean isGranted(String role) { | |||||
Authentication authentication = getSecurityContext().getAuthentication(); | |||||
if (authentication == null) return false; | |||||
for (GrantedAuthority auth : authentication.getAuthorities()) { | |||||
if (auth.getAuthority().equals("SUPERUSER")) return true; | |||||
if (role.equals(auth.getAuthority())) return true; | |||||
} | |||||
return false; | |||||
} | |||||
public static final boolean isGrantedOnly(String role) { | |||||
Authentication authentication = getSecurityContext().getAuthentication(); | |||||
if (authentication == null) return false; | |||||
for (GrantedAuthority auth : authentication.getAuthorities()) { | |||||
if (role.equals(auth.getAuthority())) return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Checks if the current user is NOT GRANTED the {@code role} | |||||
* | |||||
* @param role | |||||
* the {@code role} to check for | |||||
* @return {@code true} if the current user is NOT GRANTED the {@code role}, else {@code false} | |||||
*/ | |||||
public static final boolean isNotGranted(String role) { | |||||
return !isGranted(role); | |||||
} | |||||
/** | |||||
* Checks if the current user is GRANTED ANY of the {@code role}s | |||||
* | |||||
* @param roles | |||||
* the {@code role}s to check for | |||||
* @return {@code true} if the current user is GRANTED ANY of the {@code role}s, else {@code false} | |||||
*/ | |||||
public static final boolean isGrantedAny(String... roles) { | |||||
for (int i = 0; i < roles.length; i++) { | |||||
if (isGranted(roles[i])) return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Checks if the current user is NOT GRANTED ANY of the {@code role}s | |||||
* | |||||
* @param roles | |||||
* the {@code role}s to check for | |||||
* @return {@code true} if the current user is NOT GRANTED ANY of the {@code role}s, else {@code false} | |||||
*/ | |||||
public static final boolean isNotGrantedAny(String... roles) { | |||||
return !isGrantedAny(roles); | |||||
} | |||||
/** | |||||
* Checks if the current user is GRANTED ALL of the {@code role}s | |||||
* | |||||
* @param roles | |||||
* the {@code role}s to check for | |||||
* @return {@code true} if the current user is GRANTED ALL of the {@code role}s, else {@code false} | |||||
*/ | |||||
public static final boolean isGrantedAll(String... roles) { | |||||
for (int i = 0; i < roles.length; i++) { | |||||
if (isNotGranted(roles[i])) return false; | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* Login a user non-interactively | |||||
* | |||||
* @param userService | |||||
* any implementation of {@link UserDetailsService} | |||||
* @param username | |||||
* the username | |||||
* | |||||
* @throws UsernameNotFoundException | |||||
* if the user could not be found or the user has no GrantedAuthority | |||||
* @throws DataAccessException | |||||
* if user could not be found for a repository-specific reason | |||||
*/ | |||||
public static final void loginUser(UserDetailsService userService, String username) { | |||||
/* load the user, throw exception if user not found */ | |||||
UserDetails userDetails = userService.loadUserByUsername(username); | |||||
/* create authentication token for the specified user */ | |||||
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); | |||||
getSecurityContext().setAuthentication(authentication); | |||||
} | |||||
public static final boolean checkPwd(String pwd, int min, int max, boolean num, boolean upperEng, boolean lowerEng, boolean special) { | |||||
if (pwd == null) return false; | |||||
if (pwd.length() < min) return false; | |||||
if (pwd.length() > max) return false; | |||||
if (num && !PATTERN_DIGITS.matcher(pwd).find()) return false; | |||||
if (upperEng && !PATTERN_A2Z_UPPER.matcher(pwd).find()) return false; | |||||
if (lowerEng && !PATTERN_A2Z_LOWER.matcher(pwd).find()) return false; | |||||
if (special && !PATTERN_SPECIAL_CHARS.matcher(pwd).find()) return false; | |||||
return true; | |||||
} | |||||
public static String genPwd(int length, boolean num, boolean upperEng, boolean lowerEng, boolean special) { | |||||
if (length <= 0) return ""; | |||||
StringBuilder password = new StringBuilder(length); | |||||
Random random = new Random(System.nanoTime()); | |||||
List<String> charCategories = new ArrayList<>(4); | |||||
if (lowerEng) charCategories.add(A2Z_LOWER); | |||||
if (upperEng) charCategories.add(A2Z_UPPER); | |||||
if (num) charCategories.add(DIGITS); | |||||
if (special) charCategories.add(SPECIAL_CHARS); | |||||
for (int i = 0; i < length; i++) { | |||||
String charCategory = charCategories.get(i % charCategories.size()); | |||||
char randomChar = charCategory.charAt(random.nextInt(charCategory.length())); | |||||
if (password.length() > 0) | |||||
password.insert(random.nextInt(password.length()), randomChar); | |||||
else | |||||
password.append(randomChar); | |||||
} | |||||
return password.toString(); | |||||
} | |||||
public static void authArgs(Map<String, Object> args){ | |||||
args.put("sysGroupId", getUser().getSysGroupId()); | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.ffii.core.utils; | |||||
public class SqlUtils { | |||||
/** | |||||
* for IN() of SQL. if no parameter, it will return "''" | |||||
* | |||||
* @return 'obj1','obj2','obj3','obj4'... | |||||
*/ | |||||
public static String toInString(Object... objs) { | |||||
String rs = ""; | |||||
for (int i = 0; i < objs.length; i++) { | |||||
if (i != 0) rs += ","; | |||||
rs += "'" + objs[i] + "'"; | |||||
} | |||||
if (StringUtils.isBlank(rs)) | |||||
rs += "''"; | |||||
return rs; | |||||
} | |||||
} |
@@ -0,0 +1,71 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
/** | |||||
* String Utils based on Apache Commons StringUtils. | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class StringUtils extends org.apache.commons.lang3.StringUtils { | |||||
/** | |||||
* The String {@code "0"}. | |||||
*/ | |||||
public static final String ZERO = "0"; | |||||
/** | |||||
* The String {@code "1"}. | |||||
*/ | |||||
public static final String ONE = "1"; | |||||
/** | |||||
* The String {@code "%"}. | |||||
*/ | |||||
public static final String PERCENT = "%"; | |||||
/** | |||||
* The String {@code ","}. | |||||
*/ | |||||
public static final String COMMA = ","; | |||||
/** | |||||
* The String {@code "\r\n"} for line break on Windows | |||||
*/ | |||||
public static final String LINE_BREAK_WINDOWS = "\r\n"; | |||||
/** | |||||
* The String {@code "\n"} for line break on Unix/Linux | |||||
*/ | |||||
public static final String LINE_BREAK_LINUX = "\n"; | |||||
public static final String[] A2Z_LOWWER = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", | |||||
"w", "x", "y", "z" }; | |||||
public static final String[] A2Z_UPPER = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", | |||||
"W", "X", "Y", "Z" }; | |||||
public static final String concat(String segregator, final String... chars) { | |||||
if (segregator == null) segregator = ""; | |||||
String rs = ""; | |||||
for (String c : chars) { | |||||
if (c == null) | |||||
continue; | |||||
else { | |||||
if (StringUtils.isBlank(rs)) { | |||||
rs = c; | |||||
} else { | |||||
rs += segregator + c; | |||||
} | |||||
} | |||||
} | |||||
return rs; | |||||
} | |||||
} |
@@ -0,0 +1,54 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core2 project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils; | |||||
import java.awt.Image; | |||||
import com.google.zxing.BarcodeFormat; | |||||
import com.google.zxing.Writer; | |||||
import com.google.zxing.WriterException; | |||||
import com.google.zxing.client.j2se.MatrixToImageWriter; | |||||
import com.google.zxing.common.BitMatrix; | |||||
import com.google.zxing.oned.Code128Writer; | |||||
import com.google.zxing.qrcode.QRCodeWriter; | |||||
/** | |||||
* ZXing Utils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class ZXingUtils { | |||||
private static final Writer CODE128_WRITER = new Code128Writer(); | |||||
private static final Writer QR_CODE_WRITER = new QRCodeWriter(); | |||||
/** | |||||
* Encode contents (String) to Code 128 image | |||||
* | |||||
* @throws WriterException | |||||
*/ | |||||
public static Image encodeToImageCode128(String contents, int width, int height) throws WriterException { | |||||
BitMatrix matrix = CODE128_WRITER.encode(contents, BarcodeFormat.CODE_128, width, height); | |||||
return MatrixToImageWriter.toBufferedImage(matrix); | |||||
} | |||||
/** | |||||
* Encode contents (String) to QR Code image | |||||
* | |||||
* @throws WriterException | |||||
*/ | |||||
public static Image encodeToImageQRCode(String contents, int width, int height) throws WriterException { | |||||
BitMatrix matrix = QR_CODE_WRITER.encode(contents, BarcodeFormat.QR_CODE, width, height); | |||||
return MatrixToImageWriter.toBufferedImage(matrix); | |||||
} | |||||
} |
@@ -0,0 +1,114 @@ | |||||
package com.ffii.core.utils.sql; | |||||
import java.util.Map; | |||||
import com.ffii.core.utils.StringUtils; | |||||
/** | |||||
* <p>a query builder for handling paging </p> | |||||
* <p><b>suffix cannot blank</b></p> | |||||
* <p> | |||||
* paging arguments key: | |||||
* "start": integer (base at 0), | |||||
* "limit": integer | |||||
* </p> | |||||
* | |||||
* @see #setPaging(boolean) | |||||
* @see #setPrefix(String) | |||||
* @see #setSuffix(String) | |||||
* | |||||
* @@author Fung | |||||
*/ | |||||
public class QueryBuilder implements java.io.Serializable { | |||||
private static final long serialVersionUID = 4433680400177304974L; | |||||
// setting | |||||
private boolean paging = false; | |||||
private Map<String, Object> args; | |||||
/** "SELECT *" */ | |||||
private String prefix; | |||||
/** "FROM xxxxxxx WHERE true" */ | |||||
private String suffix; | |||||
/** default disable paging */ | |||||
public QueryBuilder(){} | |||||
public QueryBuilder(boolean paging){ | |||||
this.paging = paging; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return getSearchSql(); | |||||
} | |||||
public String getSearchSql() { | |||||
parameterCheck(); | |||||
return getPrefix() + " " + getSuffix() + (this.paging ? limitQuery() : ""); | |||||
} | |||||
/** column name "count" */ | |||||
public String getTotalSql() { | |||||
parameterCheck(); | |||||
return "SELECT COUNT(1) 'count'" + " " + getSuffix(); | |||||
} | |||||
private void parameterCheck() { | |||||
if (StringUtils.isBlank(suffix)) | |||||
throw new IllegalArgumentException("suffix of sql cannot blank"); | |||||
} | |||||
private String limitQuery() { | |||||
if (args == null) | |||||
return StringUtils.EMPTY; | |||||
else if (args.containsKey("start") && args.containsKey("limit")) | |||||
return " LIMIT :start,:limit"; | |||||
else if (args.containsKey("limit")) | |||||
return " LIMIT :limit"; | |||||
else | |||||
return StringUtils.EMPTY; | |||||
} | |||||
// setter and getter | |||||
public boolean isPaging() { | |||||
return this.paging; | |||||
} | |||||
public boolean getPaging() { | |||||
return this.paging; | |||||
} | |||||
/** true for paging query, default false*/ | |||||
public void setPaging(boolean paging) { | |||||
this.paging = paging; | |||||
} | |||||
public Map<String, Object> getArgs() { | |||||
return this.args; | |||||
} | |||||
public void setArgs(Map<String, Object> args) { | |||||
this.args = args; | |||||
} | |||||
public String getPrefix() { | |||||
return StringUtils.isNotBlank(this.prefix) ? this.prefix : "SELECT *"; | |||||
} | |||||
/** "SELECT *" */ | |||||
public void setPrefix(String prefix) { | |||||
this.prefix = prefix; | |||||
} | |||||
public String getSuffix() { | |||||
return this.suffix; | |||||
} | |||||
/** <p>"FROM xxxxxxxx WHERE true"</p>suffix cannot blank*/ | |||||
public void setSuffix(String suffix) { | |||||
this.suffix = suffix; | |||||
} | |||||
} |
@@ -0,0 +1,336 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils.web; | |||||
import java.io.IOException; | |||||
import java.util.Collections; | |||||
import java.util.Date; | |||||
import java.util.Enumeration; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import javax.servlet.ServletRequest; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import com.ffii.core.support.SimpleDateEditor; | |||||
import com.ffii.core.support.SimpleIntegerEditor; | |||||
import com.ffii.core.support.SimpleStringTrimToNullEditor; | |||||
import com.ffii.core.utils.DateUtils; | |||||
import com.ffii.core.utils.JsonUtils; | |||||
import com.ffii.core.utils.Params; | |||||
import com.ffii.core.utils.StringUtils; | |||||
import org.apache.commons.lang3.ArrayUtils; | |||||
import org.springframework.web.bind.ServletRequestBindingException; | |||||
import org.springframework.web.bind.ServletRequestDataBinder; | |||||
/** | |||||
* ServletRequestUtils (extends from Spring Framework) | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class ServletRequestUtils extends org.springframework.web.bind.ServletRequestUtils { | |||||
/** | |||||
* Mobile User-Agent Signatures | |||||
*/ | |||||
private static final String[] MOBILE_USER_AGENTS = { "Android", "iPhone", "iPad" }; | |||||
/** | |||||
* Get the <b>User-Agent</b> from the request header | |||||
* | |||||
* @param request | |||||
* HttpServletRequest | |||||
* | |||||
* @return the <b>User-Agent</b> from the request header, or <code>null</code> if not found | |||||
*/ | |||||
public static String getUserAgent(HttpServletRequest request) { | |||||
return request.getHeader("User-Agent"); | |||||
} | |||||
/** | |||||
* Check if the <b>User-Agent</b> is mobile device | |||||
* | |||||
* @param request | |||||
* HttpServletRequest | |||||
* | |||||
* @return <code>true</code> if the <b>User-Agent</b> is mobile device | |||||
*/ | |||||
public static boolean isMobileUserAgent(HttpServletRequest request) { | |||||
for (String MOBILE_USER_AGENT : MOBILE_USER_AGENTS) { | |||||
if (StringUtils.containsIgnoreCase(getUserAgent(request), MOBILE_USER_AGENT)) | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Get a Map of request parameters | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* | |||||
* @return a Map containing all of the request parameters | |||||
*/ | |||||
public static Map<String, String> getParameters(ServletRequest request) { | |||||
Enumeration<String> params = request.getParameterNames(); | |||||
Map<String, String> paramsMap = new HashMap<String, String>(); | |||||
while (params.hasMoreElements()) { | |||||
String paramName = (String) params.nextElement(); | |||||
String paramValue = request.getParameter(paramName); | |||||
paramsMap.put(paramName, paramValue); | |||||
} | |||||
return paramsMap; | |||||
} | |||||
/** | |||||
* Get an Integer parameter, with a fallback value. Never throws an exception. Can pass a distinguished value as default to enable checks of whether it was | |||||
* supplied. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the Integer value, or <code>defaultVal</code> if not present OR if the value cannot be parsed | |||||
*/ | |||||
public static Integer getIntParameter(ServletRequest request, String name, Integer defaultVal) { | |||||
try { | |||||
return getIntParameter(request, name); | |||||
} catch (ServletRequestBindingException e) { | |||||
return defaultVal; | |||||
} | |||||
} | |||||
/** | |||||
* Get a Long parameter, with a fallback value. Never throws an exception. Can pass a distinguished value as default to enable checks of whether it was | |||||
* supplied. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the Long value, or <code>defaultVal</code> if not present OR if the value cannot be parsed | |||||
*/ | |||||
public static Long getLongParameter(ServletRequest request, String name, Long defaultVal) { | |||||
try { | |||||
return getLongParameter(request, name); | |||||
} catch (ServletRequestBindingException e) { | |||||
return defaultVal; | |||||
} | |||||
} | |||||
/** | |||||
* Get a Double parameter, with a fallback value. Never throws an exception. Can pass a distinguished value as default to enable checks of whether it was | |||||
* supplied. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the Double value, or <code>defaultVal</code> if not present OR if the value cannot be parsed | |||||
*/ | |||||
public static Double getDoubleParameter(ServletRequest request, String name, Double defaultVal) { | |||||
try { | |||||
return getDoubleParameter(request, name); | |||||
} catch (ServletRequestBindingException e) { | |||||
return defaultVal; | |||||
} | |||||
} | |||||
/** | |||||
* Get a trimmed (to <code>null</code> if blank) String parameter, or <code>null</code> if not present. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* | |||||
* @return the trimmed (to <code>null</code> if blank) String value, or <code>null</code> if not present | |||||
* | |||||
* @throws ServletRequestBindingException | |||||
* a subclass of ServletException, so it doesn't need to be caught | |||||
*/ | |||||
public static String getTrimmedStringParameter(ServletRequest request, String name) throws ServletRequestBindingException { | |||||
return StringUtils.trimToNull(org.springframework.web.bind.ServletRequestUtils.getStringParameter(request, name)); | |||||
} | |||||
/** | |||||
* Get a trimmed (to <code>null</code> if blank) String parameter, with a fallback value. Never throws an exception. Can pass a distinguished value to | |||||
* default to enable checks of whether it was supplied. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the trimmed (to <code>null</code> if blank) String value, or <code>defaultVal</code> (will be trimmed to <code>null</code> if blank) if not | |||||
* present | |||||
*/ | |||||
public static String getTrimmedStringParameter(ServletRequest request, String name, String defaultVal) { | |||||
return StringUtils.trimToNull(org.springframework.web.bind.ServletRequestUtils.getStringParameter(request, name, defaultVal)); | |||||
} | |||||
/** | |||||
* Get a SQL Date parameter, or <code>null</code> if not present or cannot be parsed. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the SQL Date value, or <code>null</code> if not present or cannot be parsed | |||||
*/ | |||||
public static java.sql.Date getSqlDateParameter(ServletRequest request, String name) throws ServletRequestBindingException { | |||||
return getSqlDateParameter(request, name, null); | |||||
} | |||||
/** | |||||
* Get a SQL Date parameter, with a fallback value. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the SQL Date value, or default value if not present or cannot be parsed | |||||
*/ | |||||
public static java.sql.Date getSqlDateParameter(ServletRequest request, String name, java.sql.Date defaultVal) throws ServletRequestBindingException { | |||||
return DateUtils.toSqlDate(DateUtils.parseDateStrictly((getTrimmedStringParameter(request, name)), DateUtils.PARSE_PATTERNS, defaultVal)); | |||||
} | |||||
/** | |||||
* Get a Date parameter, or <code>null</code> if not present or cannot be parsed. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the Date value, or <code>null</code> if not present or cannot be parsed | |||||
*/ | |||||
public static Date getDateParameter(ServletRequest request, String name) throws ServletRequestBindingException { | |||||
return getDateParameter(request, name, null); | |||||
} | |||||
/** | |||||
* Get a Date parameter, with a fallback value. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* @param defaultVal | |||||
* the default value to use as fallback | |||||
* | |||||
* @return the Date value, or default value if not present or cannot be parsed | |||||
*/ | |||||
public static Date getDateParameter(ServletRequest request, String name, Date defaultVal) throws ServletRequestBindingException { | |||||
return DateUtils.parseDateStrictly((getTrimmedStringParameter(request, name)), DateUtils.PARSE_PATTERNS, defaultVal); | |||||
} | |||||
/** | |||||
* Get a List of Map from JSON string parameter. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* | |||||
* @return a List of Map, or an empty List if the parameter is not present | |||||
*/ | |||||
public static List<?> getJsonListParameter(ServletRequest request, String name) throws ServletRequestBindingException, IOException { | |||||
String content = getStringParameter(request, name); | |||||
if (content != null) | |||||
return JsonUtils.fromJsonString(content, List.class); | |||||
else | |||||
return Collections.EMPTY_LIST; | |||||
} | |||||
/** | |||||
* Get a Map from JSON string parameter. | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param name | |||||
* the name of the parameter | |||||
* | |||||
* @return a Map, or an empty Map if the parameter is not present | |||||
*/ | |||||
public static Map<?,?> getJsonMapParameter(ServletRequest request, String name) throws ServletRequestBindingException, IOException { | |||||
String content = getStringParameter(request, name); | |||||
if (content != null) | |||||
return JsonUtils.fromJsonString(content, Map.class); | |||||
else | |||||
return Collections.EMPTY_MAP; | |||||
} | |||||
/** | |||||
* Binds data using {@link ServletRequestDataBinder} with the following custom editors:- | |||||
* | |||||
* <ul> | |||||
* <li>{@link SimpleStringTrimToNullEditor}</li> | |||||
* <li>{@link SimpleIntegerEditor}</li> | |||||
* <li>{@link SimpleDateEditor}</li> | |||||
* </ul> | |||||
* | |||||
* <p> | |||||
* <b>Important:</b> The following system fields will NOT be binded for security reasons. | |||||
* <ul> | |||||
* <li>id</li> | |||||
* <li>ownerId</li> | |||||
* <li>deleted</li> | |||||
* <li>createdBy</li> | |||||
* <li>modifiedBy</li> | |||||
* <li>created</li> | |||||
* <li>modified</li> | |||||
* <li>password</li> | |||||
* </ul> | |||||
* </p> | |||||
* | |||||
* @param request | |||||
* current HTTP request | |||||
* @param object | |||||
* the target object to bind onto | |||||
* @param objectName | |||||
* the name of the target object | |||||
* @param disallowFields | |||||
* optional | |||||
*/ | |||||
public static Object doBind(ServletRequest request, Object object, String objectName, String... disallowFields) { | |||||
ServletRequestDataBinder binder = new ServletRequestDataBinder(object, objectName); | |||||
binder.registerCustomEditor(String.class, new SimpleStringTrimToNullEditor()); | |||||
binder.registerCustomEditor(Integer.class, new SimpleIntegerEditor()); | |||||
binder.registerCustomEditor(java.util.Date.class, new SimpleDateEditor()); | |||||
String[] coreFields = { Params.ID, "ownerId", "deleted", "createdBy", "modifiedBy", "created", "modified", "password" }; | |||||
binder.setDisallowedFields(ArrayUtils.addAll(coreFields, disallowFields)); | |||||
binder.bind(request); | |||||
return object; | |||||
}; | |||||
} |
@@ -0,0 +1,72 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.utils.web; | |||||
import java.io.IOException; | |||||
import java.io.OutputStreamWriter; | |||||
import java.io.UnsupportedEncodingException; | |||||
import javax.servlet.ServletOutputStream; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
/** | |||||
* ServletResponseUtils | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class ServletResponseUtils { | |||||
/** | |||||
* Writes a String to HttpServletResponse encoded with the specified charsetName, and then flush and close it. | |||||
* | |||||
* @param response | |||||
* HttpServletResponse | |||||
* @param charsetName | |||||
* The name of a supported {@link java.nio.charset.Charset </code>charset<code>} | |||||
* @param str | |||||
* String to be written | |||||
*/ | |||||
public static void writeStringToStream(HttpServletResponse response, String charsetName, String str) throws UnsupportedEncodingException, IOException { | |||||
OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream(), charsetName); | |||||
out.write(str); | |||||
out.flush(); | |||||
out.close(); | |||||
} | |||||
/** | |||||
* Writes bytes to HttpServletResponse, and then flush and close it. | |||||
* | |||||
* @param response | |||||
* HttpServletResponse | |||||
* @param bytes | |||||
* The bytes array to be written | |||||
* @throws IOException | |||||
*/ | |||||
public static void writeBytesToStream(HttpServletResponse response, byte[] bytes) throws IOException { | |||||
ServletOutputStream out = response.getOutputStream(); | |||||
out.write(bytes); | |||||
out.flush(); | |||||
out.close(); | |||||
} | |||||
/** | |||||
* Disables caching by modifying the response header. | |||||
* | |||||
* @param response | |||||
* HttpServletResponse | |||||
*/ | |||||
public static void disableCaching(HttpServletResponse response) { | |||||
response.addHeader("Pragma", "no-cache"); | |||||
response.addHeader("Cache-Control", "no-cache, no-store, max-age=0"); | |||||
response.addDateHeader("Expires", 1L); | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.web; | |||||
import org.springframework.web.context.support.WebApplicationObjectSupport; | |||||
public abstract class AbstractController extends WebApplicationObjectSupport { | |||||
} |
@@ -0,0 +1,17 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.web; | |||||
import org.springframework.web.context.support.WebApplicationObjectSupport; | |||||
public abstract class AbstractService extends WebApplicationObjectSupport { | |||||
} |
@@ -0,0 +1,10 @@ | |||||
package com.ffii.core.web; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.transaction.annotation.EnableTransactionManagement; | |||||
@Configuration | |||||
@EnableTransactionManagement | |||||
public class AppConfig { | |||||
} |
@@ -0,0 +1,35 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.web; | |||||
import org.springframework.web.bind.WebDataBinder; | |||||
import org.springframework.web.bind.annotation.InitBinder; | |||||
import com.ffii.core.support.SimpleDateEditor; | |||||
import com.ffii.core.support.SimpleIntegerEditor; | |||||
import com.ffii.core.support.SimpleStringTrimToNullEditor; | |||||
/** | |||||
* ControllerAdvice is used to define @ExceptionHandler, @InitBinder, and @ModelAttribute methods that apply to all @RequestMapping methods. | |||||
* | |||||
* @author Patrick | |||||
*/ | |||||
@org.springframework.web.bind.annotation.ControllerAdvice | |||||
public class ControllerAdvice { | |||||
@InitBinder | |||||
public void initBinder(WebDataBinder binder) { | |||||
binder.registerCustomEditor(String.class, new SimpleStringTrimToNullEditor()); | |||||
binder.registerCustomEditor(Integer.class, new SimpleIntegerEditor()); | |||||
binder.registerCustomEditor(java.util.Date.class, new SimpleDateEditor()); | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.web; | |||||
import org.apache.commons.lang3.exception.ExceptionUtils; | |||||
import org.apache.commons.logging.Log; | |||||
import org.apache.commons.logging.LogFactory; | |||||
import org.springframework.dao.DataIntegrityViolationException; | |||||
import org.springframework.http.HttpStatus; | |||||
import org.springframework.http.ResponseEntity; | |||||
import org.springframework.web.bind.annotation.ExceptionHandler; | |||||
import org.springframework.web.context.request.WebRequest; | |||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | |||||
/** | |||||
* @author Patrick | |||||
*/ | |||||
@org.springframework.web.bind.annotation.ControllerAdvice | |||||
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { | |||||
protected final Log logger = LogFactory.getLog(getClass()); | |||||
@ExceptionHandler(value = { DataIntegrityViolationException.class }) | |||||
protected ResponseEntity<Object> handleDataIntegrityViolationException(RuntimeException ex, WebRequest request) { | |||||
logger.error(ex.getMessage()); | |||||
return handleExceptionInternal(ex, ExceptionUtils.getRootCauseMessage(ex), null, HttpStatus.INTERNAL_SERVER_ERROR, request); | |||||
} | |||||
} |
@@ -0,0 +1,52 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.web.filter; | |||||
import java.io.IOException; | |||||
import javax.servlet.Filter; | |||||
import javax.servlet.FilterChain; | |||||
import javax.servlet.FilterConfig; | |||||
import javax.servlet.ServletException; | |||||
import javax.servlet.ServletRequest; | |||||
import javax.servlet.ServletResponse; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import javax.servlet.http.HttpSession; | |||||
public class AjaxSessionExpirationFilter implements Filter { | |||||
private int customSessionExpiredErrorCode = 403; | |||||
@Override | |||||
public void init(FilterConfig cfg) throws ServletException { | |||||
} | |||||
@Override | |||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { | |||||
HttpSession currentSession = ((HttpServletRequest) request).getSession(false); | |||||
if (currentSession == null) { | |||||
String requestedWithHeader = ((HttpServletRequest) request).getHeader("X-Requested-With"); | |||||
if ("XMLHttpRequest".equals(requestedWithHeader)) { | |||||
((HttpServletResponse) response).sendError(this.customSessionExpiredErrorCode); | |||||
} else { | |||||
chain.doFilter(request, response); | |||||
} | |||||
} else { | |||||
chain.doFilter(request, response); | |||||
} | |||||
} | |||||
@Override | |||||
public void destroy() { | |||||
} | |||||
} |
@@ -0,0 +1,46 @@ | |||||
/******************************************************************************* | |||||
* Copyright 2019 2Fi Business Solutions Ltd. | |||||
* | |||||
* This code is part of the Core project. | |||||
* | |||||
* This code is copyrighted. Under no circumstances should any party, people, | |||||
* or organization should redistribute any portions of this code in any form, | |||||
* either verbatim or through electronic media, to any third parties, unless | |||||
* under explicit written permission by 2Fi Business Solutions Ltd. | |||||
******************************************************************************/ | |||||
package com.ffii.core.web.view; | |||||
import org.springframework.web.servlet.View; | |||||
/** | |||||
* Abstract base class extending Spring {@link AbstractView} for {@link View} implementations. | |||||
* | |||||
* @see View | |||||
* @author Patrick | |||||
*/ | |||||
public abstract class AbstractView extends org.springframework.web.servlet.view.AbstractView { | |||||
public static final String CONTENT_TYPE_JSON = "application/json"; | |||||
public static final String CONTENT_TYPE_JAVASCRIPT = "text/javascript"; | |||||
public static final String CONTENT_TYPE_TEXT_HTML = "text/html"; | |||||
public static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain"; | |||||
public static final String CONTENT_TYPE_XML = "text/xml"; | |||||
public static final String CONTENT_TYPE_JPEG = "image/jpeg"; | |||||
public static final String CONTENT_TYPE_PNG = "image/png"; | |||||
public static final String CHARSET_UTF8 = "UTF-8"; | |||||
protected boolean disableCaching = true; | |||||
/** | |||||
* Disables caching of the generated JSON. <br> | |||||
* Default is {@code true}, which will prevent the client from caching the generated JSON. | |||||
*/ | |||||
public void setDisableCaching(boolean disableCaching) { | |||||
this.disableCaching = disableCaching; | |||||
} | |||||
} |