| @@ -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; | |||||
| } | |||||
| } | |||||